home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2005 October / PCWOCT05.iso / Software / FromTheMag / XAMPP 1.4.14 / xampp-win32-1.4.14-installer.exe / xampp / MercuryMail / RESOURCE / daemon.txt < prev    next >
Text File  |  2004-08-09  |  103KB  |  2,479 lines

  1. -----------------------------------------------------------------------
  2.  Mercury Daemon Interface for Mercury/32 v2.21 and later
  3. -----------------------------------------------------------------------
  4.  Mercury Mail Transport System,
  5.  Copyright (c) 1993-99, David Harris, All Rights Reserved.
  6. -----------------------------------------------------------------------
  7.  
  8.  
  9. Contents
  10.  
  11. 1: Introduction
  12.    1.1 - What is a Daemon?
  13.    1.2 - What uses could I have for a Daemon?
  14.  
  15. 2: Technical overview
  16.    2.1 - Basic structure of a Daemon
  17.    2.2 - What do I need to write a Daemon?
  18.    2.3 - Installing and invoking a Daemon
  19.  
  20. 3: Using the Mercury interface
  21.    3.1 - Including header files
  22.    3.2 - The M_INTERFACE structure
  23.    3.3 - Calling functions
  24.  
  25. 4: Advanced topics
  26.    4.1 - DAEMON.INI
  27.    4.2 - "Resident" Daemons
  28.    4.3 - "Global" Daemons
  29.    4.4 - Daemon configuration
  30.    4.5 - Daemon Domains
  31.  
  32. 5: Function reference
  33.    5.1 - General-purpose functions
  34.      get_variable
  35.      is_local_address
  36.      is_group
  37.      parse_address
  38.      extract_one_address
  39.      extract_cqtext
  40.      dlist_info
  41.      send_notification
  42.      get_delivery_path
  43.      get_date_and_time
  44.      write_profile
  45.      module_state
  46.  
  47.    5.2 - Job-control and management functions
  48.      ji_scan_first_job
  49.      ji_scan_next_job
  50.      ji_end_scan
  51.      ji_open_job
  52.      ji_close_job
  53.      ji_rewind_job
  54.      ji_dispose_job
  55.      ji_process_job
  56.      ji_delete_job
  57.      ji_abort_job
  58.      ji_get_job_info
  59.      ji_create_job
  60.      ji_add_element
  61.      ji_add_data
  62.      ji_get_data
  63.      ji_get_next_element
  64.      ji_set_element_status
  65.      ji_set_element_resolvinfo
  66.      ji_set_diagnostics
  67.      ji_get_diagnostics
  68.      ji_increment_time
  69.      ji_get_job_by_id
  70.      ji_get_job_times
  71.  
  72.    5.3 - Network and user database query functions
  73.      get_first_group_member
  74.      get_next_group_member
  75.      end_group_scan
  76.      is_valid_local_user
  77.      is_group_member
  78.      get_first_user_details
  79.      get_next_user_details
  80.      get_user_details
  81.      end_user_scan
  82.      read_pmprop
  83.      change_ownership
  84.      begin_single_delivery
  85.      end_single_delivery
  86.      create_object
  87.      verify_password
  88.      set_password
  89.  
  90.    5.4 - Miscellaneous functions
  91.      mercury_command
  92.      get_date_string
  93.      rfc822_time
  94.      rfc821_time
  95.      select_printer
  96.      print_file
  97.  
  98.    5.5 - File I/O and parsing routines
  99.      fm_open_file
  100.      fm_open_message
  101.      fm_close_message
  102.      fm_gets
  103.      fm_getc
  104.      fm_ungetc
  105.      fm_read
  106.      fm_getpos
  107.      fm_setpos
  108.      fm_get_folded_line
  109.      fm_find_header
  110.      fm_extract_message
  111.      parse_header
  112.      mime_prep_message
  113.      parse_mime
  114.      free_mime
  115.      fake_imessage
  116.      decode_mime_header
  117.      encode_mime_header
  118.      encode_base64_str
  119.      decode_base64_str
  120.  
  121.    5.6 - Message composition routines
  122.      om_create_message
  123.      om_dispose_message
  124.      om_add_field
  125.      om_add_attachment
  126.      om_write_message
  127.      om_send_message
  128.      Notes on composing messages
  129.  
  130.    5.7 - Statistics and logging interface
  131.      st_register_module
  132.      st_unregister_module
  133.      st_create_category
  134.      st_remove_category
  135.      st_set_hcategory
  136.      st_set_category
  137.      st_get_next_module
  138.      st_get_next_category
  139.      st_get_category_data
  140.      st_export_stats
  141.      logstring
  142.      logdata
  143.  
  144.  
  145. Appendix A: Technical issues
  146.  
  147.  
  148.  
  149. -----------------------------------------------------------------------
  150.  1: Introduction
  151. -----------------------------------------------------------------------
  152.  
  153. 1.1 - What is a Daemon?
  154.  
  155. A Mercury/32 "Daemon" (a term inherited from the unix world) is a program
  156. that provides extended services within the Mercury/32 Mail Transport
  157. System. Daemons are associated with a particular e-mail address, and
  158. when a message is sent to that address, Mercury invokes the Daemon to
  159. process it.
  160.  
  161. A Daemon has access to extensive internal Mercury services, and can
  162. easily perform complex operations such as address parsing and message
  163. creation using simple function calls.
  164.  
  165. Daemons are implemented as Windows DLLs that (in their simplest form)
  166. export a single function. There are no restrictions on what they can do,
  167. and they have access to the full range of Windows programming services.
  168.  
  169.  
  170. 1.2 - What uses could I have for a Daemon?
  171.  
  172. The most obvious use for a Daemon is to perform custom processing on mail
  173. messages; for instance, you might create a Daemon that accepts orders by
  174. e-mail, checks their validity, verifies a credit card number, then
  175. submits the order to a central database for processing. Another example:
  176. you might create a Daemon that sends faxes: when a message arrives, the
  177. Daemon looks for a fax number on the first line, then calls some other
  178. service on the workstation and asks it to send the remainder of the mail
  179. message as a fax to that number.
  180.  
  181. The only real limit on the uses a Daemon might have is your imagination
  182. and the extent to which you are prepared to do some Windows programming
  183. to realise what you have imagined.
  184.  
  185.  
  186. -----------------------------------------------------------------------
  187.  2: Technical Overview
  188. -----------------------------------------------------------------------
  189.  
  190. 2.1 - Basic structure of a Daemon
  191.  
  192. In its simplest form, a Daemon is a simple 32-bit Windows DLL that
  193. exports a single function, called "daemon". To invoke the Daemon, Mercury
  194. loads the DLL and calls the "daemon" function with a reference to the
  195. mail message (or "job"), a parameter block, and the delivery address that
  196. triggered the call (so the same Daemon can be attached to multiple e-mail
  197. addresses and can distinguish between them as required).
  198.  
  199. The prototype for the "daemon" function is as follows:
  200.  
  201.    short _export daemon (void *job, M_INTERFACE *m, char *address,
  202.       char *parameter);
  203.  
  204.    "job"  is a handle to the mail job that triggered the call to the
  205.        daemon. Using this handle, you can access the data in the message,
  206.        by passing it to functions like "ji_get_data" (see below). You
  207.        must not close or delete this job. Note: on entry to your daemon
  208.        function, this job will be open - you should not attempt to open
  209.        it using "ji_open_job", nor should you close it.
  210.  
  211.    "m" points to an M_INTERFACE structure: this structure contains pointers
  212.        to various Mercury internal functions that your Daemon can use to
  213.        parse addresses, query information and send mail.
  214.  
  215.    "address" points to the address that triggered this invocation of the
  216.        Daemon. This allows a single Daemon to service multiple addresses,
  217.        and to adjust its behaviour depending on which address it is
  218.        servicing at any given time. You must not alter the contents of
  219.        this string in any way.
  220.  
  221.     "parameter" points to any optional data specified in the Daemon's
  222.        alias entry (see below for more information on optional
  223.        parameters). This parameter will point to an empty string ("") if
  224.        there are no parameters. The maximum length of parameter data is
  225.        128 characters.
  226.  
  227. When the "daemon" function returns control to Mercury, Mercury will
  228. unload the Daemon's DLL and delete the job - no further attempt will be
  229. made to deliver it. The return value from the "daemon" function is
  230. currently ignored and must be set to 0. The return value may be
  231. meaningful in future.
  232.  
  233.  
  234. 2.2 - What do I need to write a Daemon?
  235.  
  236. A Daemon is simply a standard Windows DLL that exports a single entry
  237. point; as such, you can use most standard tools to create one. The
  238. functions exported to the Daemon via the M_INTERFACE structure all use
  239. the C calling convention and expect C-type parameters (so, strings are
  240. NUL-terminated arrays of characters).
  241.  
  242. The most logical tools for developing Daemons are Borland C++ v4.5 or
  243. later, Borland C++/Builder, or Microsoft Visual C++. The sample code
  244. provided with this documentation has been developed and tested using
  245. Borland C++ v5.02.
  246.  
  247.  
  248. 2.3 - Installing and invoking a Daemon
  249.  
  250. Installing a Daemon so that Mercury can invoke it requires the creation
  251. of an alias in a special form. The reason Daemons can only be invoked
  252. via an alias is to ensure that only approved Daemons are run, for
  253. security reasons. Aliases can be easily created and maintained from
  254. within Mercury/32, using the "Aliases" option on the "Configuration"
  255. menu.
  256.  
  257. The alias must be of the following form:
  258.  
  259.    daemon_address@host.domain == daemon:path_to_dll[;parameter]
  260.  
  261. "daemon_address" should be whatever address will invoke the Daemon, while
  262. "path_to_dll" should be the fully-qualified path to the Daemon's DLL
  263. file. If your Daemon needs a parameter passed to it when its "daemon"
  264. function is invoked, you can specify that parameter by placing a
  265. semicolon after the filename, followed by the text you want your Daemon
  266. to receive. The maximum length of the parameter is 128 characters.
  267.  
  268. Example: you wish to install a Daemon called "cookie"; the DLL file for
  269. the Daemon is "c:\mercury\daemons\cookie.dll", and your domain is
  270. "biscuit.com". You would create the following alias:
  271.  
  272.    cookie@biscuit.com == daemon:c:\mercury\daemons\cookie.dll
  273.  
  274. If the Daemon's DLL file is found in the same directory as MERCURY.EXE,
  275. then you can omit the path from the DLL's file specification.
  276.  
  277. Daemons can add aliases to or remove aliases from the system alias file
  278. by themselves: an elegantly-written Daemon would provide a configuration
  279. interface that automates the process of adding the aliases, rather than
  280. relying on the user to do it.
  281.  
  282.  
  283. -----------------------------------------------------------------------
  284.  3: Using the Mercury interface
  285. -----------------------------------------------------------------------
  286.  
  287. 3.1 - Including header files
  288.  
  289. In order to use the Mercury Daemon interface, you will need to add the
  290. following line near the top of your C or C++ source file:
  291.  
  292. #include "daemon.h"
  293.  
  294. Note that you must include this line *after* you have included the
  295. standard "windows.h" master header file.
  296.  
  297.  
  298. 3.2 - The M_INTERFACE structure
  299.  
  300. When your Daemon is invoked, Mercury passes it a large structure called
  301. an M_INTERFACE. This structure contains some static data, and a number of
  302. pointers to internal Mercury functions that your Daemon can call.
  303.  
  304.    "dsize"   The "dsize" parameter is the size of the M_INTERFACE
  305.       structure in bytes. A Daemon can use this as part of a version-
  306.       checking process.
  307.  
  308.    "vmajor"  The major version number of the copy of Mercury that is
  309.       running. For instance, for Mercury v2.15, this value will be "2".
  310.  
  311.    "vminor"  The minor version number of the copy of Mercury that is
  312.       running. For instance, for Mercury v2.15, this value will be "15".
  313.  
  314.  
  315. 3.3 - Calling functions
  316.  
  317. The remaining variables in the M_INTERFACE structure are pointers to
  318. functions within Mercury that the Daemon can call to access core
  319. services. To call one of these functions, simply use the pointer as if it
  320. were a normal function - so, if you want to call the "get_variable"
  321. function to retrieve the GV_QUEUENAME variable, you would use this line
  322. of code
  323.  
  324.    char *str
  325.    str = (char *) (m->get_variable (GV_QUEUENAME));
  326.  
  327. Note the C cast to a (char *) - this simply suppresses compiler warnings,
  328. because the "get_variable" function always returns its results as a DWORD
  329. value.
  330.  
  331.  
  332. -----------------------------------------------------------------------
  333.  4: Advanced topics
  334. -----------------------------------------------------------------------
  335.  
  336. 4.1 - DAEMON.INI
  337.  
  338. For advanced Daemon functions such as Residency and Configuration,
  339. Mercury scans a file called DAEMON.INI for parameters. DAEMON.INI uses a
  340. slightly different syntax from MERCURY.INI, in that it uses "=" as a
  341. separator between keyword and parameter instead of the ":" used in
  342. MERCURY.INI. This design difference is intended to allow installers and
  343. Daemons to use the Windows WritePrivateProfileString and
  344. GetPrivateProfileString API functions to update and access the file.
  345.  
  346.  
  347. 4.2 - "Resident" Daemons
  348.  
  349. There may be occasions when a Daemon operates better by remaining in
  350. memory at all times, instead of being loaded and unloaded as required.
  351. Internally, a "Resident" Daemon is no different from a normal Daemon,
  352. except that it can optionally export a "startup" function that Mercury
  353. will call the when the Daemon is loaded.
  354.  
  355. To install a "Resident" Daemon, add a [Daemons] section to a file called
  356. DAEMON.INI in the same directory as MERCURY.EXE, and include a name and the
  357. full path to your Daemon's DLL on a line in that section.
  358.  
  359.    Example: your Daemon is called "Cookie Daemon" and is located in
  360.    C:\MERCURY\DAEMONS\COOKIE.DLL
  361.  
  362.    [Daemons]
  363.    Cookie Daemon = c:\mercury\daemons\cookie.dll
  364.  
  365. Mercury will load the Daemon at startup and will not unload it until
  366. exiting. The Daemon can export a function with the following prototype:
  367.  
  368.    short _export startup (M_INTERFACE *mi, UINT_32 *flags,
  369.       char *name, char *param)
  370.  
  371. If this function is present in the DLL, Mercury will call it with an
  372. Interface block as soon as the DLL is loaded. Note that the Interface
  373. block is not persistent - if you need to store it for later use, you must
  374. allocate your own storage and make a copy of the structure in it.
  375.  
  376. The "flags" parameter to "startup" is a location where the Daemon can
  377. return certain indicators about itself. At the time of writing this
  378. specification, no flag values are defined - you should write 0 into the
  379. location pointed to by the "flags" pointer.
  380.  
  381. The "name" parameter is the name string defined for the Daemon in
  382. DAEMON.INI - you will typically use this if you have to create a message
  383. box or error dialog.
  384.  
  385. The "param" parameter to "startup" is an optional string, presumably
  386. containing configuration or run data for your Daemon. You can specify a
  387. parameter by placing a semicolon and the parameter after the name of the DLL
  388. in the entry in the [Daemons] section; so, using our Cookie Daemon above
  389. as an example, if you wanted to pass the parameter "autocookie" to the
  390. startup function, you would have an entry like this in DAEMON.INI:
  391.  
  392.    [Daemons]
  393.    Cookie Daemon = c:\mercury\daemons\cookie.dll;autocookie
  394.  
  395. If a Daemon has no parameters, an empty string ("") will be passed in
  396. "param". The maximum length of parameter data is 128 characters.
  397.  
  398. The "daemon" function of a Resident Daemon is called in exactly the same
  399. way as it would be for a normal Daemon - the only difference is that the
  400. Daemon's DLL is not loaded and unloaded as part of the process. A
  401. Resident Daemon will commonly create a copy of the job in a file using
  402. the "fm_extract_message" function, then spin off a thread to process the
  403. file at its leisure.
  404.  
  405. Resident Daemons may fire threads at will if they wish to perform routine
  406. processing (this is usually done from the "startup" function). All the
  407. functions in the M_INTERFACE parameter block are thread-safe.
  408.  
  409. Resident Daemons may export a function called "closedown": if they do,
  410. Mercury will call it as part of its shutdown process prior to unloading
  411. the resident Daemon. The function should have this protoype:
  412.  
  413.    short _export closedown (M_INTERFACE *m, DWORD code,
  414.       char *name, char *param);
  415.  
  416. The "code" parameter is reserved for future use and should be ignored
  417. at present. The return from this function is currently unused but must
  418. be set to 0. Only Resident Daemons (including "Global" Daemons) will
  419. have this function called.
  420.  
  421. The "name" parameter is the name string defined for the Daemon in
  422. DAEMON.INI - you will typically use this if you have to create a message
  423. box or error dialog.
  424.  
  425. The "param" parameter is the same as was passed to the "startup" function
  426. - see above for more details. If a Daemon has no parameters, an empty
  427. string ("") will be passed in "param".  The maximum length of parameter
  428. data is 128 characters.
  429.  
  430.  
  431. 4.3 - "Global" Daemons
  432.  
  433. A "Global" Daemon is a specialized form of Resident Daemon that is
  434. passed all messages processed by the Mercury core module. Global Daemons
  435. are called before any other processing is done on the job, and can
  436. instruct Mercury to process, delete or defer a job through their return
  437. value. Examples of uses for Global Daemons include a Daemon that scans
  438. all incoming and outgoing mail for viruses, or a Daemon that makes
  439. archival copies of all incoming and outgoing mail for auditing purposes.
  440.  
  441. To install a "Global" Daemon, add a [Global Daemons] section to a file
  442. called DAEMON.INI in the same directory as MERCURY.EXE, and include a
  443. name and the full path to your Daemon's DLL on a line in that section.
  444.  
  445.    Example: your Daemon is called "Spam Killer" and is located in
  446.    C:\MERCURY\DAEMONS\SPAMKILL.DLL
  447.  
  448.    [Global Daemons]
  449.    Spam Killer = c:\mercury\daemons\spamkill.dll
  450.  
  451. Global Daemons are always Resident Daemons - see the preceding section
  452. for information on this. When a Global Daemon's "daemon" function is
  453. called, the "address" parameter is always set to "[Global]".
  454.  
  455. A Global Daemon may not delete a job directly, but it can return the
  456. following values to Mercury to control the processing of the job:
  457.  
  458.    0  - Process the job normally
  459.    1  - Delete the job without further processing
  460.    2  - Defer the job for the system defer time
  461.  
  462. If a Global Daemon instructs Mercury to delete a job, the job is deleted
  463. at once, without further ado. No error notification is sent, nor is there
  464. any indication other than a comment on the core module console display
  465. that the job has been killed.
  466.  
  467.  
  468. 4.4 - Daemon configuration
  469.  
  470. Daemons may wish to add a configuration option to the Mercury
  471. "Configuration" menu to allow the user to change settings or otherwise
  472. control the behaviour of the Daemon. To do this, add a [Daemon_Config]
  473. section to a file called DAEMON.INI in the same directory as MERCURY.EXE,
  474. and include a name and the full path to your Daemon's configuration DLL
  475. on a line in that section. The name is used to create your Daemon's
  476. configuration menu line.
  477.  
  478.    Example: your Daemon's configuration module is called "Cookie Daemon"
  479.    and is located in C:\MERCURY\DAEMONS\COOKIECF.DLL
  480.  
  481.    [Daemon_Config]
  482.    Cookie Daemon = c:\mercury\daemons\cookiecf.dll
  483.  
  484. The configuration DLL can be either your Daemon's main processing DLL, or
  485. a subsidiary DLL that only performs configuration functions - the choice
  486. of which method to use will depend on your needs.
  487.  
  488. Mercury will add your Daemon's name to its Configuration menu, and when
  489. the option is selected, will load your DLL and look for a function with
  490. the following prototype:
  491.  
  492.    short _export configure (M_INTERFACE *mi, char *name,
  493.       char *param);
  494.  
  495. "M_INTERFACE" is a regular parameter block. "name" is the name defined
  496. in the [Daemon_Config] section (this allows the same DLL to service
  497. multiple functions keyed on the name).
  498.  
  499. The "param" parameter to "config" is an optional string, presumably
  500. containing configuration or run data for your Daemon. You can specify a
  501. parameter by placing a semicolon and the parameter after the name of the
  502. DLL in the entry in the [Daemons] section; so, using our Cookie Daemon
  503. above as an example, if you wanted to pass the parameter "autoconfig" to
  504. the config function, you would have an entry like this in DAEMON.INI:
  505.  
  506.    [Daemon_Config]
  507.    Cookie Daemon = c:\mercury\daemons\cookiecf.dll;autoconfig
  508.  
  509. If a Daemon has no parameters, an empty string ("") will be passed in
  510. "param". The maximum length of parameter data is 128 characters.
  511.  
  512. If your DLL refers to a Resident Daemon, Mercury will look for the
  513. configuration function in the loaded copy and will not load the DLL
  514. again. If your DLL needs to be loaded to perform configuration, its
  515. "startup" function will not be called.
  516.  
  517. Both resident and non-resident Daemons can use configuration services if
  518. they wish. What the "configure" function does when called is up to the
  519. individual developer, but it is normal for it to show a dialog allowing
  520. the user to change its configuration options.
  521.  
  522.  
  523. 4.5 - Daemon Domains
  524.  
  525. Occasionally, you may wish to have a Daemon that services all mail sent
  526. to a particular subdomain. As an example, imagine a fax server Daemon
  527. that treats the address portion as a fax number: for a server of this
  528. kind, you would want to be able to send any "username" and have it
  529. processed by the Daemon.
  530.  
  531. To create a "Daemon Domain" of this kind, select the "Mercury Core Module"
  532. configuration option on the "Configuration" menu, and locate the group of
  533. controls near the bottom of the dialog labelled "Domains recognized as
  534. local by this server". Create a new domain entry where the "Domain name"
  535. portion is the domain name to be handled by your Daemon, and the
  536. "Host/server" portion is "daemon:path_to_dll". Once this domain entry has
  537. been created, all mail sent to any user at the domain you have defined
  538. will result in the Daemon being invoked.
  539.  
  540.    Example: you have a fax server Daemon, C:\MERCURY\FAX.DLL; you want
  541.    all mail addressed to "fax.biscuit.com" to be passed to this Daemon.
  542.    You would create the following domain definition:
  543.  
  544.    Host/Server                  Domain name
  545.    daemon:c:\mercury\fax.dll    fax.biscuit.com
  546.  
  547. Note that this kind of operation will almost always require special name
  548. server entries called "MX Entries" to advertise the domain name - contact
  549. your service provider for more details on creating MX entries.
  550.  
  551.  
  552. -----------------------------------------------------------------------
  553.  5: Function reference
  554. -----------------------------------------------------------------------
  555.  
  556. 5.1 - General-purpose functions
  557.  
  558.    -------------------------------
  559.    DWORD get_variable (int index);
  560.    -------------------------------
  561.  
  562.    Returns the value of an internal Mercury variable. The following
  563.    values can be passed for "index":
  564.  
  565.       GV_QUEUENAME (1)       (Returns "char *")
  566.          The name of the directory where Mercury's queue manager is
  567.          looking for jobs. The return value is a (char *) pointing to
  568.          a path, which may be in either drive letter or UNC format.
  569.  
  570.       GV_SMTPQUEUENAME (2)   (Returns "char *")
  571.          The name of the directory where Mercury's queue manager is
  572.          looking for outgoing mail jobs. This variable is not meaningful
  573.          under current versions of Mercury and should not be used.
  574.  
  575.       GV_MYNAME (3)          (Returns "char *")
  576.          The Internet name for this copy of Mercury (the domain portion
  577.          it will use when forming addresses).
  578.  
  579.       GV_TTYFONT (4)         (Returns "HFONT")
  580.          A handle to the font Mercury is currently using to draw text
  581.          in console listings.
  582.  
  583.       GV_MAISERNAME (5)      (Returns "char *")
  584.          The name of the Mercury mail server (usually "MAISER")
  585.  
  586.       GV_FRAMEWINDOW (6)     (Returns "HWND")
  587.          The handle to the Mercury Frame window: Daemons can use this
  588.          value to send messages to the Mercury core process.
  589.  
  590.       GV_SYSFONT (7)         (Returns "HFONT")
  591.          A handle to the font Mercury is using by default to draw
  592.          controls and general dialog text.
  593.  
  594.       GV_BASEDIR (8)         (Returns "char *")
  595.          Returns the directory from which MERCURY.EXE was run.
  596.  
  597.       GV_SCRATCHDIR (9)      (Returns "char *")
  598.          Returns a temporary working directory if one has been defined
  599.          in Mercury. If no temporary directory has been defined, this
  600.          value returns the same value as "GV_BASEDIR".
  601.  
  602.    Daemons must treat all returned values as read-only and must not
  603.    attempt to modify them.
  604.  
  605.  
  606.    --------------------------------------------------------------
  607.    int is_local_address (char *address, char *uic, char *server);
  608.    --------------------------------------------------------------
  609.  
  610.    Determine whether or not the address in "address" refers to a local
  611.    account (i.e, one to which Mercury can complete final delivery).
  612.  
  613.    "uic"      Receives the local username matching the address when
  614.               the function is successful. Allocate at least 256
  615.               characters for this string.
  616.  
  617.    "server"   Receives network-specific information associated with the
  618.               local user when the function is successful. This value may
  619.               have to be passed to other functions. Allocate at least
  620.               256 characters for this string.
  621.  
  622.    Returns:   2 if the address is non-local but served by an alias
  623.               1 if the address is local;
  624.               0 if the address is not local;
  625.              -1 on error (local domain but no such user)
  626.  
  627.    This function resolves aliases and synoyms, and performs any
  628.    necessary network lookups to validate the address.
  629.  
  630.  
  631.    ---------------------------------------------------------------
  632.    int is_group (char *address, char *host, char *gname);
  633.    ---------------------------------------------------------------
  634.  
  635.    Determine whether the address contained in "address" refers to a valid
  636.    group to which Mercury can perform delivery.
  637.  
  638.    "address"  The simplified form of the address, with no domain portion
  639.               (so, pass "everyone", not "everyone@host.domain").
  640.  
  641.    "host"     Receives network-specific host information when the
  642.               function is successful. You may need to pass this value
  643.               to other functions.
  644.  
  645.    "gname"    Receives the name of the group on success.
  646.  
  647.    Returns    1 if the address refers to a known group
  648.               0 if the address does not refer to a known group
  649.  
  650.  
  651.    ----------------------------------------------------------
  652.    int parse_address (char *target, char *source, int limit);
  653.    ----------------------------------------------------------
  654.  
  655.    Reduce an RFC822 address to its simplest form, by discarding any
  656.    textual components it contains.
  657.  
  658.    Example
  659.       "David Harris" (Pegasus Mail Author) <david@pmail.gen.nz>
  660.    would be reduced by this function to
  661.       david@pmail.gen.nz
  662.  
  663.    "source"   points to the address that is to be reduced. This string
  664.               is not changed as part of the process.
  665.  
  666.    "target"   points to the location where the reduced address should be
  667.               written. You may pass the same value as "source" if you
  668.               want to overwrite the address with the reduced form.
  669.  
  670.    "limit"    the maximum length of the resulting string. You should
  671.               pass the allocated size of "target" less one.
  672.  
  673.    Returns    1 if the reduction was successful
  674.               0 on error (the address was malformed in some way)
  675.  
  676.  
  677.    ---------------------------------------------------------------
  678.    int extract_one_address (char *dest, char *source, int offset);
  679.    ---------------------------------------------------------------
  680.  
  681.    Given a string potentially containing multiple addresses separated
  682.    by commas, extract the next address from the string into "dest".
  683.    The first time you call this function, pass zero for "offset"; for
  684.    each subsequent call, pass the value returned by the previous call
  685.    to the function, until it returns 0.
  686.  
  687.    "dest"     receives the next address from the string
  688.  
  689.    "source"   The string containing comma-separated addresses
  690.  
  691.    "offset"   0 on the first call, the return from the previous call
  692.               to the function on subsequent calls.
  693.  
  694.    Returns:   > 0 on success ("dest" contains a valid address)
  695.               0 when no more addresses exist ("dest" is invalid)
  696.  
  697.  
  698.    --------------------------------------------------------
  699.    void extract_cqtext (char *dest, char *source, int len);
  700.    --------------------------------------------------------
  701.  
  702.    Given a string containing a single address, strip out the address
  703.    part and return only the extra textual information.
  704.  
  705.    Example:
  706.       "David Harris" <david@pmail.gen.nz>
  707.    will be reduced by this function to
  708.       David Harris
  709.  
  710.    "dest"     receives the reduced textual form
  711.  
  712.    "source"   points to the address to reduce
  713.  
  714.    "len"      the maximum number of characters to write into "dest".
  715.  
  716.    Returns:   Nothing.
  717.  
  718.  
  719.    ------------------------------------------------------
  720.    int dlist_info (DLIST *dlist, char *lname, int number,
  721.       char *address, char *errbuf, LIST *modlist);
  722.    ------------------------------------------------------
  723.  
  724.    Returns information about the distribution list named in "lname".
  725.    For information on the DLIST data structure, see "daemon.h".
  726.  
  727.    "dlist"    points to a DLIST data structure into which the data
  728.               for the list should be written.
  729.  
  730.    "lname"    points to the simple name of the list (no domain part).
  731.  
  732.    "number"   if "lname" is NULL, then this variable is used to
  733.               determine which list to retrieve. Calling functions
  734.               can iterate through all the lists on the server by
  735.               setting "lname" to NULL then repeatedly incrementing
  736.               this variable in successive calls.
  737.  
  738.    "address"  if non-NULL, contains a simplified address form which
  739.               should be compared with the list of moderators for the
  740.               list. If the address matches any moderator in the list,
  741.               the "matched" field of the DLIST structure will be set
  742.               to 1.
  743.  
  744.    "errbuf"   receives a textual error message if the function fails.
  745.               Allocate at least 128 characters for this variable.
  746.  
  747.    "modlist"  points to a LIST data structure that receives a list
  748.               of all the moderators defined for the list. This value
  749.               can be NULL.
  750.  
  751.    Returns:   0 on success
  752.              -1 on failure
  753.  
  754.    Note the non-standard return value convention for this function.
  755.  
  756.  
  757.    -------------------------------------------------------------------
  758.    void send_notification (char *username, char *host, char *message);
  759.    -------------------------------------------------------------------
  760.  
  761.    Send a short, one-line message directly to the given user.
  762.  
  763.    "username" a username, as returned by "is_local_user"
  764.  
  765.    "host"     network-specific host information as returned by
  766.               "is_local_user".
  767.  
  768.    "message"  A message of up to 65 characters to send to the user
  769.  
  770.    Returns    Nothing
  771.  
  772.    This function only works if a network enabler supporting broadcast
  773.    messages (such as the NetWare 3.x and 4.x enablers) has been installed
  774.    and is currently loaded in Mercury/32.
  775.  
  776.  
  777.    ---------------------------------------------------------------
  778.    int get_delivery_path (char *path, char *username, char *host);
  779.    ---------------------------------------------------------------
  780.  
  781.    Retrieve the delivery path for the specified user/host combination.
  782.    "user" and "host" should be the values returned by "is_local_address".
  783.  
  784.    "path"     Receives the directory in which files should be created
  785.               for final delivery to the specified user. The string will
  786.               not end in "\" or "/". Allocate at least 256 characters
  787.               for this string
  788.  
  789.    Returns:   1 if "path" contains a valid delivery path
  790.               0 on error ("path" is invalid)
  791.  
  792.  
  793.    ---------------------------------
  794.    int get_date_and_time (BYTE *tm);
  795.    ---------------------------------
  796.  
  797.    Get the current date and time, querying the file server for the
  798.    information if possible. The information is written into a seven
  799.    byte structure laid out as follows:
  800.  
  801.       Byte 0  - Years since 1900 (i.e, 2000 == 100)
  802.       Byte 1  - Month (ie, January == 1)
  803.       Byte 2  - Day (1 .. 31)
  804.       Byte 3  - Hour (0 - 24)
  805.       Byte 4  - Minute (0 - 59)
  806.       Byte 5  - Second (0 - 60)
  807.       Byte 6  - Day of week (Sunday == 0)
  808.  
  809.    This function will obtain the date and time from the local workstation
  810.    if no server connection is available to provide it.
  811.  
  812.    Returns:   1 if the time and date were correctly retrieved
  813.               0 on system error.
  814.  
  815.  
  816.    -----------------------------------------------
  817.    int write_profile (char *section, char *fname);
  818.    -----------------------------------------------
  819.  
  820.    Write a profile section into MERCURY.INI. "section" should contain
  821.    the name of the section to write, enclosed in square brackets (so,
  822.    if you want to write a section called "CookieServ", you would pass
  823.    "[CookieServ]" as the "section" parameter). "fname" should be a full
  824.    path to a file containing the data to write into the section. The
  825.    data is written exactly as it appears and may use any syntax you
  826.    wish, although it is conventional to use "keyword : parameter".
  827.    The data in the file is expected to be line-oriented, and no single
  828.    line may exceed 254 characters.
  829.  
  830.    Returns:   1 on success
  831.               0 on failure ("fname" is not accessible)
  832.  
  833.  
  834.    -----------------------------------------------------------
  835.    int module_state (char *modname, int set_value, int state);
  836.    -----------------------------------------------------------
  837.  
  838.    Query or set the state for a protocol module. "modname" should point
  839.    to a string identifying the module name of the module whose state is
  840.    to be queried or set. If "set_value" is non-zero, then this function
  841.    call will attempt to set the module's state to the value contained
  842.    in "state". If "set_value" is zero, then "state" is ignored, and the
  843.    module's current state is returned.
  844.  
  845.    A module's state is a bitmap where the low sixteen bits are reserved
  846.    and the high sixteen bits can be defined by individual modules for
  847.    whatever purpose they wish. The reserved bits have the following
  848.    possible values:
  849.  
  850.       Bit 0:  Online state, 1 = online, 0 = offline.
  851.       Bit 1:  ("get" only) 1 = busy, 0 = idle.
  852.  
  853.    Returns:  -1:  No such module
  854.              -2:  Module does not support this operation
  855.          < -255:  Module-specific error return value
  856.            >= 0:  Previous state (success)
  857.  
  858.  
  859.  
  860. 5.2 - Job control and interface functions
  861.  
  862. The functions in this section allow you to create, scan and manipulate
  863. mail jobs in the Mercury queue.
  864.  
  865.  
  866.    ----------------------------------------------------------
  867.    void *ji_scan_first_job (int type, int mode, void **data);
  868.    void * ji_scan_next_job (void **data);
  869.    void ji_end_scan (void **data);
  870.    ----------------------------------------------------------
  871.  
  872.    Routines for enumerating jobs in the queue. First, call
  873.    "ji_scan_first_job" and if it returns a non-NULL value, call
  874.    "ji_scan_next_job" until NULL is returned. A non-NULL return value
  875.    is the job handle for the job that is found - use this handle in
  876.    calls to other job management functions.
  877.  
  878.       "type"   Can have any one of the following values:
  879.                JT_GENERAL    Messages for local delivery
  880.                JT_OUTGOING   Messages scheduled for off-server delivery
  881.                JT_ANY        Any type of message
  882.  
  883.  
  884.       "mode"   Used to select jobs in particular states - choose from:
  885.                JE_READY      Messages ready for processing
  886.                JE_COMPLETED  Messages where all processing is finished
  887.                JE_FAILED     Messages with delivery failures pending
  888.                JE_ANY        Messages in any state
  889.  
  890.    The "data" parameter is used by these routines to store state
  891.    information, and should be passed as part of each call. If
  892.    "ji_scan_first_job" returns a non-NULL value, then you *must* call
  893.    "ji_end_scan" when you have finished scanning with jobs, even if you
  894.    do not completely iterate through all the jobs in the queue.
  895.  
  896.    None of these routines actually opens the job handle - you must call
  897.    ji_open_job, passing the job handle, if you need to access the data in
  898.    the job or modify its settings.
  899.  
  900.    Returns:    NULL if no further jobs exist
  901.                non-NULL (the job handle) on success
  902.  
  903.  
  904.    ----------------------------------
  905.    int ji_open_job (void *jobhandle);
  906.    ----------------------------------
  907.  
  908.    Open a job. You must call this function before calling any function
  909.    that accesses the job's data or changes the job's settings. The job
  910.    is opened and locked, which will prevent it from being processed
  911.    during normal polling operations, and will prevent other processes
  912.    from opening it.
  913.  
  914.    The job handle passed to this call should be a value returned by
  915.    "ji_scan_first_job", "ji_scan_next_job" or "ji_create_job".
  916.  
  917.    Returns:    1 if the job was opened successfully.
  918.                0 on failure.
  919.  
  920.  
  921.    ----------------------------------
  922.    int ji_close_job (void *jobhandle);
  923.    ----------------------------------
  924.  
  925.    Close a job opened with "ji_open_job". The job is unlocked and
  926.    released for normal processing. Other processes may access the job
  927.    once this routine has been called.
  928.  
  929.    Calling this routine forces the job's overall status to be updated
  930.    (so, a job may change from JE_READY to JE_COMPLETED after this
  931.    function has been called).
  932.  
  933.    Returns:    1 on success
  934.                0 on failure (job invalid).
  935.  
  936.  
  937.    ------------------------------------------------
  938.    void ji_rewind_job (void *jobhandle, int flags);
  939.    ------------------------------------------------
  940.  
  941.    "Rewind" a job - reposition its file pointer at 0. "flags" can have
  942.    either of two values, "JR_CONTROL", in which case the control
  943.    information for the job is reset to the first address element, or
  944.    "JR_DATA", in which case the data associated with the file is set
  945.    for reading from the start.
  946.  
  947.    Example: say you are scanning through the addresses to which a message
  948.       should be sent using "ji_get_next_element", and you strike a
  949.       condition that means that you need to start processing from the
  950.       first address element in the message again, you would call this
  951.       function with the "JR_CONTROL" parameter.
  952.  
  953.  
  954.    -------------------------------------
  955.    int ji_dispose_job (void *jobhandle);
  956.    -------------------------------------
  957.  
  958.    Call this function when you have finished working with a job handle
  959.    returned by ji_scan_first_job, ji_scan_next_job, or ji_create_job.
  960.    This routine deallocates memory associated with the job - it does not
  961.    delete or in any other manner change the actual disk structures
  962.    associated with the job. This function must be called for every
  963.    non-NULL return value from the functions listed above.
  964.  
  965.    Returns:    1 on success
  966.                0 on failure (job invalid).
  967.  
  968.  
  969.    -------------------------------------
  970.    int ji_process_job (void *jobhandle);
  971.    -------------------------------------
  972.  
  973.    Indicates to the job manager that a formal attempt to process the
  974.    job has begun. The retry count for the job is incremented and (if
  975.    applicable) the last retry time is updated. Daemons should seldom
  976.    if ever have any cause to call this function.
  977.  
  978.    Returns:    1 on success
  979.                0 on failure (invalid or closed job handle)
  980.  
  981.  
  982.    ------------------------------------
  983.    int ji_delete_job (void *jobhandle);
  984.    ------------------------------------
  985.  
  986.    Permanently remove a job from the queue. This routine deletes all
  987.    files associated with the job (including diagnostic files) and then
  988.    discards the job handle. It is invalid to access the job handle after
  989.    it has been passed to this routine.
  990.  
  991.    You should not call ji_dispose_job for handles passed to this routine.
  992.  
  993.    Returns:  1 on success
  994.              0 on failure (invalid job handle)
  995.  
  996.  
  997.    ----------------------------------------------
  998.    int ji_abort_job (void *jobhandle, int fatal);
  999.    ----------------------------------------------
  1000.  
  1001.    Abort processing a job. If the job was created using ji_create_job,
  1002.    then it is discarded, otherwise its retry count is incremented by the
  1003.    system default retry increment, the job is closed, and the job handle
  1004.    is invalidated. It is an error to use or access the job handle after
  1005.    calling this routine.
  1006.  
  1007.    If "fatal" is non-zero, then the job is unilaterally failed (normally,
  1008.    a job is only marked as failed if any of its addresses cannot be
  1009.    delivered - setting this flag will fail even a job where all the
  1010.    recipients have successfully received the mail).
  1011.  
  1012.    You should not call ji_dispose_job for handles passed to this routine.
  1013.  
  1014.    Returns:  1 on success
  1015.              0 on failure (job handle was invalid)
  1016.  
  1017.  
  1018.    ---------------------------------------------------
  1019.    int ji_get_job_info (void *jobhandle, JOBINFO *ji);
  1020.    ---------------------------------------------------
  1021.  
  1022.    Get information about the specified job. The fields in the JOBINFO
  1023.    structure are filled out with information from the current element
  1024.    in the message's control stream.
  1025.  
  1026.       typedef struct
  1027.          {
  1028.          int structlen;      //  Size of this structure
  1029.          char jobstatus;     //  JS_READY, JS_COMPLETE or JS_FAILED
  1030.          long jobflags;      //  Mercury internal processing flags
  1031.          char status;        //  As jobstatus but for current element only
  1032.          char *from;         //  Envelope address for message (read-only)
  1033.          char *to;           //  Recipient address for current element
  1034.          long dsize;         //  Total RFC822 size of the message
  1035.          long rsize;         //  Number of bytes read since last rewind
  1036.          int total_rcpts;    //  Total recipients for message
  1037.          int total_failures; //  Total failed recipients to date
  1038.          int total_retries;  //  Total recipients marked for retry
  1039.          char ip1 [16];      //  Primary IP address for current element
  1040.          char ip2 [16];      //  Secondary IP address for "   "    "
  1041.          char ip3 [16];      //  Third-choice IP address for  "   "   "
  1042.          char ip4 [16];      //  Fourth-choice IP address for "   "   "
  1043.          char jobid [20];    //  Unique identifier for message
  1044.          } JOBINFO;
  1045.  
  1046.    Returns:  1 on success
  1047.              0 on failure
  1048.  
  1049.  
  1050.    ----------------------------------------------------------------------
  1051.    void *ji_create_job (int type, char *from, unsigned char *start_time);
  1052.    ----------------------------------------------------------------------
  1053.  
  1054.    Create a job in the mail queue.
  1055.  
  1056.    "type" can be either JT_GENERAL, or JT_OUTGOING. You should usually
  1057.       use "JT_GENERAL" unless you are explicitly creating a job which is
  1058.       to be processed directly by the MercuryC SMTP client.
  1059.  
  1060.    "from" is the address Mercury should write in the envelope of the
  1061.       message.
  1062.  
  1063.    "start_time" is a date and time in the 7-byte format described under
  1064.       "get_date_and_time", which specifies the earliest time at which the
  1065.       job can be processed. If NULL, the job can be processed as soon as
  1066.       it has been closed.
  1067.  
  1068.    The job handle returned by this function can be passed to any other
  1069.    job management function in order to manipulate the queue job.
  1070.  
  1071.    Returns:   non-NULL on success (the job handle)
  1072.               NULL on failure.
  1073.  
  1074.  
  1075.    ----------------------------------------------------
  1076.    int ji_add_element (void *jobhandle, char *address);
  1077.    ----------------------------------------------------
  1078.  
  1079.    Add an address to a job created using "ji_create_job". "address"
  1080.    should be the full e-mail address of a single recipient for the
  1081.    message. You should call this function for each address to which the
  1082.    message should be sent.
  1083.  
  1084.    Returns:   1 on success
  1085.               0 on failure (invalid job handle)
  1086.  
  1087.  
  1088.    ----------------------------------------------
  1089.    int ji_add_data (void *jobhandle, char *data);
  1090.    ----------------------------------------------
  1091.  
  1092.    Add data for the message body to a job created using "ji_create_job".
  1093.    You should call this function repeatedly for the headers and text of
  1094.    the message until all the data has been written. This function does
  1095.    not modify the data in any way - it is up to you to ensure that line
  1096.    endings are correct (CR/LF pairs) and that the data conforms to the
  1097.    standards for Internet mail. There is a clear expectation that the
  1098.    data for the message will be passed to this function a line at a time.
  1099.  
  1100.    Note: When you use this function to build an outgoing mail message,
  1101.    you are effectively building the message exactly as it will be sent -
  1102.    it is up to you to add all the necessary RFC822 headers, the blank
  1103.    line that separates them from the message body, and the message body
  1104.    itself.
  1105.  
  1106.    Returns:   1 on success
  1107.               0 on failure (invalid job handle)
  1108.  
  1109.  
  1110.    --------------------------------------------------------------
  1111.    char *ji_get_data (void *jobhandle, char *buffer, int buflen);
  1112.    --------------------------------------------------------------
  1113.  
  1114.    Get the next line of data from the current job. "buffer" should be
  1115.    allocated to be at least 1024 characters in order to comply with
  1116.    RFC821, but any length will be honoured. The line returned will
  1117.    usually end in a CRLF pair, but may not if the buffer was too
  1118.    small to accommodate the entire line.
  1119.  
  1120.    Returns:  "buffer" on success
  1121.               NULL on failure (end of data)
  1122.  
  1123.  
  1124.    -----------------------------------------------------
  1125.    char *ji_get_next_element (void *jobhandle, int type,
  1126.       JOBINFO *jobinfo);
  1127.    -----------------------------------------------------
  1128.  
  1129.    Get the next element from the job. Information about the element
  1130.    is returned in "jobinfo". "type" can be JE_ANY for any type of entry,
  1131.    JE_READY for the next entry marked as "ready for processing", or
  1132.    JE_FAILED for the next entry marked as failed.
  1133.  
  1134.    Returns:   The e-mail address associated with the entry on success
  1135.               NULL on failure, or no more elements.
  1136.  
  1137.  
  1138.    -----------------------------------------------------
  1139.    int ji_set_element_status (void *jobhandle, int mode,
  1140.       unsigned char *date);
  1141.    -----------------------------------------------------
  1142.  
  1143.    Set the status fields of the current job element. "mode" can be one
  1144.    of the following values:
  1145.  
  1146.       JS_COMPLETED   -  "date" is ignored
  1147.       JS_FAILED      -  "date" is ignored
  1148.       JS_RETRY       -  "date" is used for requeuing if non-NULL
  1149.       JS_PENDING     -  "date" is ignored
  1150.  
  1151.    "JS_PENDING" status indicates a status that should be reset to
  1152.    "JS_RETRY" if a job is aborted: it is set by the SMTP client
  1153.    module when the remote SMTP server has accepted a RCPT TO: line
  1154.    for the address, and is needed so that the job can be "rolled
  1155.    back" if there's a subsequent network error.
  1156.  
  1157.    Returns:  1 on success
  1158.              0 on failure (invalid job handle or element)
  1159.  
  1160.  
  1161.    ----------------------------------------------------------------------
  1162.    int ji_set_element_resolvinfo (void *jobhandle, char *ip1, char *ip2);
  1163.    ----------------------------------------------------------------------
  1164.  
  1165.    Set the resolver information fields of the current job element. "ip1"
  1166.    and "ip2" point to ASCII versions of IP addresses for servers that
  1167.    should be used to deliver the element within the job. Daemons should
  1168.    never need to use this function.
  1169.  
  1170.    Returns:  1 on success
  1171.              0 on failure (invalid job handle or element)
  1172.  
  1173.  
  1174.    ------------------------------------------------------------------
  1175.    int ji_set_diagnostics (void *jobhandle, int forwhat, char *text);
  1176.    ------------------------------------------------------------------
  1177.  
  1178.    Set the diagnostic field of the current job element to the text
  1179.    contained in "text". It is legal (and probably even essential)
  1180.    to call this function repeatedly; each time it is called, a line
  1181.    is added to the diagnostic stream for the entry.
  1182.  
  1183.      "forwhat" can be JD_ELEMENT to set information specific to
  1184.       the current element, or JD_JOB to set diagnostics pertaining
  1185.       to the job in general.
  1186.  
  1187.    Returns:  1 on success
  1188.              0 on failure (bad job handle or element record)
  1189.  
  1190.  
  1191.    -------------------------------------------------------------------
  1192.    int ji_get_diagnostics (void *jobhandle, int forwhat, char *fname);
  1193.    -------------------------------------------------------------------
  1194.  
  1195.    Get the diagnostics for the job or current job element. "fname"
  1196.    should point to a buffer at least 128 characters long where the
  1197.    diagnostic information should be placed: the buffer will be filled
  1198.    in with a filename by this routine. If this routine returns 2,
  1199.    it is the calling routine's responsibility to delete the file in
  1200.    "fname" when it is finished with the data; if this routine returns
  1201.    1, then the calling routine must NOT delete the file in "fname".
  1202.    If 0 is returned, then no diagnostic information is available
  1203.    for the element.
  1204.  
  1205.      "forwhat" can be JD_ELEMENT to retrieve information specific to
  1206.       the current element, or JD_JOB to retrieve diagnostics pertaining
  1207.       to the job in general.
  1208.  
  1209.    Returns: 2 "fname" is valid - delete it when finished
  1210.             1 "fname" is valid - do NOT delete it when finished
  1211.             0 "fname" is invalid - no diagnostic information available.
  1212.  
  1213.  
  1214.    ------------------------------------------------------------------
  1215.    void ji_increment_time (unsigned char *tm, unsigned int add_secs);
  1216.    ------------------------------------------------------------------
  1217.  
  1218.    Add "add_secs" seconds to the date and time specified in "tm",
  1219.    which should be in the format described under "get_date_and_time".
  1220.    You can subtract seconds from a time by passing a negative value for
  1221.    "add_secs" to this function.
  1222.  
  1223.  
  1224.    ----------------------------------
  1225.    void *ji_get_job_by_id (char *id);
  1226.    ----------------------------------
  1227.  
  1228.    Given the ID number of a job (the "jobid" element of a JOBSTRUCT
  1229.    structure), return a usable handle for that job if it still exists in
  1230.    the queue. This function allows you to re-open a job by reference
  1231.    without holding on to its jobhandle between calls. If this function is
  1232.    successful, the job handle it returns can be used in any other ji_*
  1233.    function expecting a job handle. The job handle is not open on return.
  1234.  
  1235.    Returns: Non-NULL on success
  1236.             NULL on failure (job no longer exists, or is locked).
  1237.  
  1238.  
  1239.    ---------------------------------------------------------------------
  1240.    int ji_get_job_times (void *jobhandle, char *submitted, char *ready);
  1241.    ---------------------------------------------------------------------
  1242.  
  1243.    Get the time of original submission for a job and the time it is
  1244.    scheduled for processing. "submitted" and "ready" are the submission
  1245.    and ready times respectively, in the same format as returned by
  1246.    get_date_and_time. You can pass NULL for either of these parameters if
  1247.    you do not require the value. The ready time for a job can be
  1248.    inspected at any time, but the submission time for a job can only be
  1249.    retrieved if the job has been successfully opened using ji_open_job.
  1250.  
  1251.    Returns: Non-zero on success
  1252.             0 on failure (invalid job handle)
  1253.  
  1254. 5.3 - Network and user database query functions
  1255.  
  1256.  
  1257.    ------------------------------------------------------------------
  1258.    int get_first_group_member (char *group, char *host, char *member,
  1259.       int mlen, void **data);
  1260.    int get_next_group_member (char *member, int mlen, void **data);
  1261.    int end_group_scan (void **data);
  1262.    ------------------------------------------------------------------
  1263.  
  1264.    Routines for enumerating the members of a group. First, call
  1265.    "get_first_group_member", passing the name of the group in "group".
  1266.    The host and username information will be returned in "host" and
  1267.    "member" respectively, with "mlen" controlling the maximum nunber of
  1268.    characters written into the member name (allocate at least 128
  1269.    characters for both "host" and "member"). If "get_first_group_member"
  1270.    returns non-zero, call "get_next_group_member" repeatedly to iterate
  1271.    through the group's membership. When you have finished scanning
  1272.    through the membership, call "end_group_scan". You should pass the
  1273.    same address for "data" to all calls - these functions store state
  1274.    information there.
  1275.  
  1276.    If "get_first_group_member" returns a non-zero value, then you must
  1277.    call "end_group_scan" when you have finished scanning, whether or
  1278.    not you actually reach the end of the group's membership.
  1279.  
  1280.  
  1281.  
  1282.    --------------------------------------------------------------------
  1283.    int is_valid_local_user (char *address, char *username, char *host);
  1284.    --------------------------------------------------------------------
  1285.  
  1286.    Check whether "username" identifies a valid local user. This function
  1287.    differs from "is_local_address" in that it does not resolve aliases
  1288.    or synonyms, and will only return a success result if the address
  1289.    represents a genuine, existing local user. Note that the "address"
  1290.    parameter must have no domain portion - this routine expects a user
  1291.    name portion only.
  1292.  
  1293.    As with "is_local_address", this routine may modify the "username"
  1294.    and "host" parameters - allocate at least 256 characters for each
  1295.    of these parameters.
  1296.  
  1297.    Returns   1 if the address is a valid local user
  1298.              0 if the address does not represent a known user
  1299.  
  1300.  
  1301.    ------------------------------------------------------------------
  1302.    int is_group_member (char *host, char *username, char *groupname);
  1303.    ------------------------------------------------------------------
  1304.  
  1305.    Determine whether or not a user is a member of a specified group.
  1306.  
  1307.    Returns:  1: the group exists and the user is a member
  1308.              0: the group exists and the user is not a member
  1309.             -1: the group does not exist
  1310.  
  1311.  
  1312.    --------------------------------------------------------------------
  1313.    int get_first_user_details (char *host, char *match, char *username,
  1314.       int ulen, char *address, int alen, char *fullname, int flen,
  1315.       void **data);
  1316.    int get_next_user_details (char *username, int ulen, char *address,
  1317.       int alen, char *fullname, int flen, void **data);
  1318.    int end_user_scan (void **data);
  1319.    --------------------------------------------------------------------
  1320.  
  1321.    Enumerate the local users on the current system.
  1322.  
  1323.       "match" - currently ignored; pass an empty string.
  1324.       "username" - receives at most "ulen" bytes of the user's
  1325.          login name
  1326.       "address" - receives at most "alen" bytes of the user's
  1327.          e-mail address.
  1328.       "fullname" - receives at most "flen" bytes of the user's
  1329.          full (personal) name.
  1330.  
  1331.    If "get_first_user_details" returns a non-NULL value, then you must
  1332.    call "end_user_scan" when you have finished scanning users, whether
  1333.    or not you actually reach the end of the user list.
  1334.  
  1335.    Returns:  1 if valid user details were found and returned
  1336.              0 on error, or if no further users exist.
  1337.  
  1338.  
  1339.    --------------------------------------------------------------------
  1340.    int get_user_details (char *host, char *match, char *username,
  1341.       int ulen, char *address, int alen, char *fullname, int flen);
  1342.    --------------------------------------------------------------------
  1343.  
  1344.    Return information about a single specific user, whose username is
  1345.    contained in "match". All other parameters are the same as those in
  1346.    "get_first_user_details".
  1347.  
  1348.    Returns:  1 on success
  1349.              0 on failure (no such user)
  1350.  
  1351.  
  1352.    ---------------------------------------------------------
  1353.    void read_pmprop (char *userid, char *server, PMPROP *p);
  1354.    ---------------------------------------------------------
  1355.  
  1356.    Get extended features settings for the specified user. "Extended
  1357.    features" is a Pegasus Mail term and covers things like automatic
  1358.    mail forwarding and address synonyms. Any given user will not
  1359.    necessarily have an extended features record.
  1360.  
  1361.    The principal field of the "PMPROP" structure from Mercury's
  1362.    point of view is the "gw_auto_forward", which contains the address
  1363.    to which all mail delivered by Mercury should be forwarded.
  1364.  
  1365.  
  1366.    ---------------------------------------------------------------
  1367.    int change_ownership (char *fname, char *host, char *newowner);
  1368.    ---------------------------------------------------------------
  1369.  
  1370.    Change the network ownership of the specified file. This function
  1371.    is only meaningful when Mercury is using a network interface
  1372.    module that supports the concept of changing the ownership of a
  1373.    file.
  1374.  
  1375.    Returns:  1 if the file's ownership was successfully changed
  1376.              0 on error, or if the function is not supported.
  1377.  
  1378.  
  1379.    -----------------------------------------------------------------
  1380.    int begin_single_delivery (char *uic, char *server, void **data);
  1381.    void end_single_delivery (void **data)
  1382.    -----------------------------------------------------------------
  1383.  
  1384.    These functions should not be called by Daemons; they are only
  1385.    meaningful to true Mercury protocol modules.
  1386.  
  1387.  
  1388.    -----------------------------------------------------------
  1389.    INT_32 create_object (char *objectname, INT_32 objecttype,
  1390.       char *id, INT_32 flags);
  1391.    -----------------------------------------------------------
  1392.  
  1393.    Create a new object in the system. "objectname" can be any valid
  1394.    filename for the system in use, but must not contain spaces, and
  1395.    should be 8 characters or less if compatibility with 16-bit systems
  1396.    is required. "id" is the object's default identification (or personal
  1397.    name) string.
  1398.  
  1399.       "objecttype" can be OBJ_USER, or OBJ_ADMINISTRATOR
  1400.  
  1401.       "flags" is a bitmap containing user creation control flags. The
  1402.          following values are possible:
  1403.          1  Copy any default messages into the new user's mailbox
  1404.  
  1405.    This routine will fail if a user already exists with the name "username",
  1406.    or if the user's mail directory cannot be created.
  1407.  
  1408.    This routine is only available in Standalone Mercury systems - it is
  1409.    not implemented in Network-level plugins.
  1410.  
  1411.    Returns:  1 on success
  1412.              0 on failure
  1413.  
  1414.  
  1415.    ----------------------------------------------------------------
  1416.    int verify_password (char *username, char *host, char *password,
  1417.       int select);
  1418.    ----------------------------------------------------------------
  1419.  
  1420.    Given a username and host as returned by "is_local_user", verify
  1421.    a password for that user.
  1422.  
  1423.    "password" The password to verify. This may or may not be case-
  1424.               sensitive, depending on the underlying operating system.
  1425.  
  1426.    "select"   indicates the type of password to verify: this variable
  1427.               can have the following values
  1428.  
  1429.                  SYSTEM_PASSWORD (1)   The user's login password
  1430.                  APOP_SECRET (2)       The user's APOP shared secret
  1431.  
  1432.    Returns:  1 if the password appears to be valid
  1433.              0 if the password is invalid
  1434.  
  1435.  
  1436.    ----------------------------------------------------------------
  1437.    INT_32 set_password (char *username, char *host, char *password,
  1438.       INT_32 select);
  1439.    ----------------------------------------------------------------
  1440.  
  1441.    Set the user's System password or APOP secret. "username" and "host"
  1442.    should be values returned by a previous call to either
  1443.    "is_valid_local_user" or "get_*_user_details". "select" should be
  1444.    either APOP_SECRET or SYSTEM_PASSWORD. This function may not be
  1445.    available on all systems.
  1446.  
  1447.    Returns:  1 on success
  1448.              0 on failure
  1449.  
  1450.  
  1451.  
  1452. 5.4 - Miscellaneous functions
  1453.  
  1454.  
  1455.    -----------------------------------------------------------------
  1456.    DWORD mercury_command (DWORD selector, DWORD parm1, DWORD parm2);
  1457.    -----------------------------------------------------------------
  1458.  
  1459.    This function provides an extended interface into Mercury's core
  1460.    services. In general, less-frequently-used functions, or functions
  1461.    with simple parameter lists may be exposed via this mechanism.
  1462.  
  1463.    The function operates much like the Windows "SendMessage" function:
  1464.    you indicate which function is required in the "selector" parameter,
  1465.    and the meanings of the "parm1" and "parm2" parameters will depend
  1466.    on the selector. The return from this function also depends on the
  1467.    selector value. Mercury does not return from this function until
  1468.    the command has completed.
  1469.  
  1470.    At the time of writing, the following selectors and their parameter
  1471.    lists are defined; others will be added over time:
  1472.  
  1473.       Selector:  GET_MODULE_INTERFACE
  1474.       Parm1:     (char *) Pointer to name of module to locate
  1475.       Parm2:     Unused, must be 0
  1476.       Returns:   Protocol module command interface function pointer
  1477.       Comments:  This message is only for use by Mercury protocol
  1478.                  modules and should not be used by Daemons.
  1479.  
  1480.       Selector:  ADD_ALIAS
  1481.       Parm1:     (char *) Pointer to alias to add
  1482.       Parm2:     (char *) Pointer to address to associate with alias
  1483.       Returns:   Non-zero on success, 0 on failure
  1484.       Comments:  Add an alias to the system alias file. This function
  1485.                  will fail if an alias already exists using the string
  1486.                  supplied. Any type of alias may be added, including
  1487.                  Daemon:, File: and TFile: aliases.
  1488.  
  1489.       Selector:  DELETE_ALIAS
  1490.       Parm1:     (char *) Pointer to alias to delete
  1491.       Parm2:     Unused, must be 0
  1492.       Returns:   Non-zero on success, 0 on failure
  1493.       Comments:  Deletes the specified alias from the system alias file.
  1494.                  A Daemon that adds its own aliases for the addresses
  1495.                  it services will typically call this function before
  1496.                  adding its alias.
  1497.  
  1498.       Selector:  RESOLVE_ALIAS
  1499.       Parm1:     (char *) Pointer to alias to resolve
  1500.       Parm2:     (char *) Pointer to buffer to place address
  1501.       Returns:   Non-zero on match, 0 on no match or failure
  1502.       Comments:  Attempts to resolve the specified alias to its real-
  1503.                  world address form. You must allocate at least 180
  1504.                  characters to the buffer (parm2) parameter.
  1505.  
  1506.       Selector:  RESOLVE_SYNONYM
  1507.       Parm1:     (char *) Pointer to synonym to resolve
  1508.       Parm2:     (char *) Pointer to buffer to place address
  1509.       Returns:   Non-zero on match, 0 on no match or failure
  1510.       Comments:  Attempts to resolve the specified string as a
  1511.                  synonym (two-way alias). For more information on
  1512.                  synonyms, see the Mercury documentation. You must
  1513.                  allocate at least 180 characters for the buffer
  1514.                  (parm2) parameter.
  1515.  
  1516.       Selector:  QUEUE_STATE
  1517.       Parm1:     0 to query current state, 1 to set state
  1518.       Parm2:     1 to pause processing, 0 to enable it
  1519.       Returns:   The state of queue processing prior to the call
  1520.       Comments:  Allows you to pause and unpause the core module's
  1521.                  processing of the mail queue.
  1522.  
  1523.  
  1524.  
  1525.    ----------------------------------------------------------------
  1526.    char *get_date_string (int selector, char *buf, BYTE *datetime);
  1527.    ----------------------------------------------------------------
  1528.  
  1529.    Return a properly-formatted date string.
  1530.  
  1531.      "selector"    - either RFC_821_TIME or RFC_822_TIME
  1532.      "buf"         - where the formatted string should be written
  1533.      "datetime"    - NULL for the current time, or a 7-byte time structure
  1534.  
  1535.    RFC821 time format uses a 2-digit year and is only used in timestamp
  1536.    headings in "Received" headers for messages.
  1537.  
  1538.    RFC822 time is conventional RFC822/RFC1123 date format, using a
  1539.      4-digit year.
  1540.  
  1541.    Both formats include a time zone if one is defined on the system.
  1542.  
  1543.    "buf" must be at least 40 characters in length.
  1544.  
  1545.    See "get_date_and_time" for a description of the format of "datetime"
  1546.  
  1547.    Returns:  buf
  1548.  
  1549.  
  1550.    ---------------------------------
  1551.    char *rfc822_time (char *buffer);
  1552.    char *rfc821_time (char *buffer);
  1553.    ---------------------------------
  1554.  
  1555.    Provided for backwards-compatibility only. Use "get_date_string"
  1556.    instead of these functions.
  1557.  
  1558.  
  1559.    ------------------------------------------------------
  1560.    INT_32 select_printer (char *device_name, int maxlen);
  1561.    ------------------------------------------------------
  1562.  
  1563.    Bring up a dialog that prompts the user to select a printer from
  1564.    those available on the current system. Both local and network
  1565.    printers are listed. The printer name is returned in "device_name"
  1566.    if the user clicks "OK". The name returned is suitable for use in
  1567.    the "print_file" function (see below).
  1568.  
  1569.    Returns:         1 if OK was clicked
  1570.                     0 if Cancel was clicked
  1571.  
  1572.  
  1573.    ---------------------------------------------------------------------
  1574.    INT_32 (* PRINT_FILE) (char *fname, char *printername, UINT_32 flags,
  1575.       INT_32 lrmargin, INT_32 tbmargin, char *title, char *username,
  1576.       char *fontname, INT_32 fontsize);
  1577.    ---------------------------------------------------------------------
  1578.  
  1579.    Print a file or mail message. This function provides a simple way of
  1580.    printing textual data, and has useful formatting options designed to
  1581.    handle normal textual mail messages. Only textual data can be printed
  1582.    - this routine will not print HTML, RTF or other formatted data types.
  1583.  
  1584.       "fname" is the full pathname of the file to print
  1585.  
  1586.       "printername" is the name of the printer on which the file is to be
  1587.          printed; this can be any local or network printer. You can use the
  1588.          "select_printer" function (see above) to allow the user to choose
  1589.          a printer. If you pass NULL for this parameter, Mercury will use
  1590.          the system's default printer.
  1591.  
  1592.       "flags" is a bitmap of flags controlling the way the file will be
  1593.          printed; the following values are available:
  1594.  
  1595.             PRT_MESSAGE    Print as an RFC822 mail message
  1596.             PRT_REFORMAT   Reformat long lines when printing
  1597.             PRT_TIDY       Print only "important" headers
  1598.             PRT_FOOTER     Print a footer on each page
  1599.             PRT_NOHEADERS  Print no message headers
  1600.             PRT_FIRSTONLY  Print only first line of headers
  1601.             PRT_ITALICS    Print quoted text in italics
  1602.  
  1603.       "lrmargin" is the margin in millmetres (mm, 25.4mm == 1 inch) to
  1604.          allow at the left and right sides of the printed page.
  1605.  
  1606.       "tbmargin" is the margin in millimetres to allow at the top and
  1607.          bottom of the printed page.
  1608.  
  1609.       "title" is an optional string Mercury will use to identify the
  1610.          document in the Windows print manager queue; the title is not
  1611.          itself printed anywhere on the document. This paramter can be
  1612.          NULL if not required.
  1613.  
  1614.       "username" is the username Mercury should print in the footer on
  1615.          each page if that option is selected. This parameter can be NULL
  1616.          if it is not required.
  1617.  
  1618.       "fontname" is the name of the font to use when printing. If this
  1619.          parameter is NULL or zero-length, the default font and size will
  1620.          be used.
  1621.  
  1622.       "fontsize" is the size in points of the printer font. This
  1623.          parameter is ignored if "fontname" is NULL or zero-length.
  1624.    
  1625.    Returns:         1 on successful printing
  1626.                     0 on printing error.
  1627.  
  1628.  
  1629.  
  1630. 5.5 - File I/O and parsing routines
  1631.  
  1632. The functions in this section allow Daemons to perform message and MIME
  1633. parsing on mail messages. The first group of functions allow the
  1634. manipulation of files in a portable manner (since open file handles
  1635. cannot be passed between DLLs and programs), while the second group of
  1636. functions perform parsing and analysis of complex mail messages.
  1637.  
  1638.  
  1639.    ------------------------------------------------
  1640.    INT_32 fm_open_file (char *path, UINT_32 flags);
  1641.    ------------------------------------------------
  1642.  
  1643.    Open any file. The file is opened in binary mode, so line endings
  1644.    are returned as CRLF, not as simply LFs. The handle returned by this
  1645.    function can be passed to any of the parsing or file manipulation
  1646.    functions that expect an internal file handle.
  1647.  
  1648.    "flags" can have any of the following values:
  1649.  
  1650.       FF_NO_LINE_ENDINGS - tells "fm_gets" to remove CRLF terminators
  1651.         from each line it reads from the file
  1652.  
  1653.    Returns:  > 0 (the file reference) on success
  1654.              0 on failure
  1655.  
  1656.  
  1657.    -----------------------------------------------------
  1658.    INT_32 fm_open_message (IMESSAGE *im, UINT_32 flags);
  1659.    -----------------------------------------------------
  1660.  
  1661.    Provided for backwards compatibility with Pegasus Mail. Daemons should
  1662.    use "fm_open_file" instead of calling this function.
  1663.  
  1664.  
  1665.    ---------------------------------
  1666.    int fm_close_message (INT_32 id);
  1667.    ---------------------------------
  1668.  
  1669.    Close a file opened using "fm_open_file" or "fm_open_message".
  1670.  
  1671.    Returns:  1 on success
  1672.              0 on failure
  1673.  
  1674.  
  1675.    --------------------------------------------------
  1676.    char (*fm_gets (char *buf, INT_32 max, INT_32 id);
  1677.    --------------------------------------------------
  1678.  
  1679.    Read the next line from a file opened using "fm_open_file" or
  1680.    "fm_open_message". At most "max - 1" characters are read from the
  1681.    file, and the line is always nul-terminated. CRLF characters will
  1682.    be present unless the file was opened using the "FF_NO_LINE_ENDINGS"
  1683.    flag, or possibly for the last line of the file.
  1684.  
  1685.    Returns:  "buf" on success
  1686.              NULL on failure or EOF.
  1687.  
  1688.  
  1689.    ---------------------------
  1690.    INT_16 fm_getc (INT_32 id);
  1691.    ---------------------------
  1692.  
  1693.    Read the next character from the file.
  1694.  
  1695.    Returns:   the character on success
  1696.               EOF on failure
  1697.  
  1698.  
  1699.    -------------------------------------
  1700.    void fm_ungetc (INT_16 c, INT_32 id);
  1701.    -------------------------------------
  1702.  
  1703.    "unget" the last character retrieved using "fm_gets" or "fm_getc".
  1704.    Exactly one level of character may be "ungot" using this function.
  1705.  
  1706.  
  1707.    ---------------------------------------------------------
  1708.    INT_32 fm_read (INT_32 id, char *buffer, INT_32 bufsize);
  1709.    ---------------------------------------------------------
  1710.  
  1711.    Read at most "bufsize" bytes from the file. No character conversions
  1712.    are done, and the data may not necessarily end on a line boundary.
  1713.  
  1714.    Returns:  > 0 on success (the number of bytes read)
  1715.              = 0 on EOF
  1716.              < 0 on error
  1717.  
  1718.  
  1719.    ------------------------------
  1720.    INT_32 fm_getpos (INT_32 fil);
  1721.    ------------------------------
  1722.  
  1723.    Get the current offset in the open file. The return from this function
  1724.    may be passed to "fm_setpos" to reposition the file at a given point.
  1725.  
  1726.    Returns:  Current file offset.
  1727.  
  1728.  
  1729.    ---------------------------------------------
  1730.    INT_16 fm_setpos (INT_32 fil, INT_32 offset);
  1731.    ---------------------------------------------
  1732.  
  1733.    Reposition the file pointer for an open file. "offset" must be a
  1734.    value returned by "fm_getpos", or 0 to rewind the file. You should
  1735.    not assume a linear relationship between "offset" and the data in the
  1736.    file.
  1737.  
  1738.    Returns:  > 0 on success
  1739.              0 on error.
  1740.  
  1741.  
  1742.    --------------------------------------------------------------
  1743.    INT_32 fm_get_folded_line (INT_32 fil, char *line, int limit);
  1744.    --------------------------------------------------------------
  1745.  
  1746.    Read a header from a message, "unfolding" continuation lines as
  1747.    required. This function understands RFC822 line folding rules for
  1748.    message headers, and will return a single line containing the full
  1749.    extent of a header. This is an extremely useful function for reading
  1750.    through the headers of a message without worrying about dealing with
  1751.    continuation lines. This routine guarantees that the returned line
  1752.    will be nul-terminated and will not end in CRLF.
  1753.  
  1754.    There is usually no reason to use this function to read the body of
  1755.    a mail message, and doing so may lead to unexpected results.
  1756.  
  1757.    Example: if the next lines in the message are:
  1758.  
  1759.        From: David Harris
  1760.          <david@pmail.gen.nz>
  1761.          (Pegasus Mail Author)
  1762.  
  1763.    Then this function will return the following single string:
  1764.  
  1765.        From: David Harris <david@pmail.gen.nz> (Pegasus Mail Author)
  1766.  
  1767.    Returns:  > 0 on success (number of characters in line)
  1768.              0 if the end of headers has been reached.
  1769.  
  1770.  
  1771.    -------------------------------------------------------------------
  1772.    char (*fm_find_header (INT_32 fil, char *name, char *buf, int len);
  1773.    -------------------------------------------------------------------
  1774.  
  1775.    Locate the header whose keyword is passed in "name" in the specified
  1776.    message file. The entire line without the keyword is returned, with any
  1777.    continuations unfolded into the data.
  1778.  
  1779.    Returns:  "buf" on success (points to start of field body)
  1780.              NULL on failure.
  1781.  
  1782.  
  1783.    -----------------------------------------------------------
  1784.    int fm_extract_message (void *job, char *fname, int flags);
  1785.    -----------------------------------------------------------
  1786.  
  1787.    Take an open Mercury mail job and extract its contents to the file
  1788.    specified in "fname".
  1789.  
  1790.    "flags" can have combinations of the following values:
  1791.  
  1792.       FFX_APPEND       - append to the file if it exists - don't overwrite
  1793.       FFX_NO_HEADERS   - omit the message headers when writing to the file
  1794.       FFX_TIDY_HEADERS - write only "significant" headers to the file
  1795.       FFX_NOT_OPEN     - the job is not currently open
  1796.  
  1797.    If the "FFX_NOT_OPEN" flag is specified, then this routine will open
  1798.    and close the job. When passing the "job" parameter supplied to the
  1799.    "daemon" function, you must not specify this flag - the job is already
  1800.    open and must remain open during processing.
  1801.  
  1802.  
  1803.    -------------------------------------------
  1804.    int parse_header (INT_32 fil, IMESSAGE *m);
  1805.    -------------------------------------------
  1806.  
  1807.    Perform comprehensive parsing on the open message file "fil", which
  1808.    should have been opened using "fm_open_file". The information gleaned
  1809.    from the message's headers is stored in the IMESSAGE structure, "m".
  1810.    Applications using this function will be particularly interested in
  1811.    the "flags" field of this structure, since it contains extensive
  1812.    information about attachments and MIME formatting that might be
  1813.    present in the message.
  1814.  
  1815.    Returns:  1 on success
  1816.              0 on failure (very rare)
  1817.  
  1818.  
  1819.    ---------------------------------------------------------------
  1820.    int mime_prep_message (INT_32 fil, char *fname, int headers);
  1821.    ---------------------------------------------------------------
  1822.  
  1823.    Given a non-multipart MIME message, create a "sanitized" version of
  1824.    the message data in the file named by "fname". MIME encodings are
  1825.    decoded as required, and any character set conversions that might
  1826.    be needed are performed. If you pass an empty string for "fname",
  1827.    Mercury will create a temporary file for you, returning the name
  1828.    of that file in "fname".
  1829.  
  1830.    Note that you must not call this function for a multipart message:
  1831.    to extract a sanitized section from a multipart message, call
  1832.    "parse_mime", traverse the multipart list until you find the section
  1833.    you need, then pass that section to "fake_imessage".
  1834.  
  1835.    If "headers" is non-zero, then the message headers from the message
  1836.    will be included in the output file, otherwise they will be omitted.
  1837.  
  1838.    Returns:  > 0 on success
  1839.              0 on failure
  1840.  
  1841.  
  1842.    ----------------------------------------
  1843.    int parse_mime (INT_32 fil, IMIME *m);
  1844.    ----------------------------------------
  1845.  
  1846.    Given any valid MIME message, parse its contents and return them in
  1847.    the "IMIME" structure, "m".
  1848.  
  1849.    The IMIME structure is a relatively complicated variable record which
  1850.    can represent any MIME format, including arbitrarily nested Multipart
  1851.    MIME messages. The fields in an IMIME structure are as follows:
  1852.  
  1853.       primary     - the logical message type, e.g MP_TEXT
  1854.       secondary   - the logical message sub-type, e.g. MPT_PLAIN
  1855.       encoding    - the encoding for this part, e.g ME_BASE_64
  1856.       disposition - either MD_ATTACHMENT or MD_INLINE
  1857.       p_string    - the actual primary type string, e.g "TEXT"
  1858.       s_string    - the actual secondary sub-type string, e.g "PLAIN"
  1859.       description - "Content-description" for this part, if any
  1860.       section     - the number of this section within the message
  1861.       fname       - the filename associated with this part, if any
  1862.  
  1863.       d           - a union, one data element from which will contain
  1864.                     part-specific information about this part. You
  1865.                     should select the appropriate record based on the
  1866.                     value of "primary".
  1867.                        primary == MP_TEXT        : use "mpt"
  1868.                        primary == MP_MULTIPART   : use "mpp"
  1869.                        primary == MP_APPLICATION : use "mpa"
  1870.                        primary == MP_MESSAGE     : use "mpm"
  1871.  
  1872.    The "mpm" record within the structure contains a list of other parts
  1873.    in the message. It should be traversed by dereferencing the "top"
  1874.    field of the "partlist" entry and casting its data field to "IMIME *".
  1875.  
  1876.       Example: code to traverse the parts in a multipart message
  1877.  
  1878.       IMIME *m, *m2;
  1879.       LNODE *cur;
  1880.  
  1881.       if (m->primary == MP_MULTIPART)
  1882.          {
  1883.          for (cur = m->d.mpp.partlist.top; cur != NULL; cur = cur->next)
  1884.             {
  1885.             m2 = (IMIME *) (cur->data);
  1886.             //  do whatever you need with that section here.
  1887.             }
  1888.          }
  1889.  
  1890.    Note that it is valid, or even normal for a sub-part of a multipart
  1891.    message to be itself a Multipart item. When this happens, the parts
  1892.    of the nested item will have been properly parsed into its own "mpp"
  1893.    structure, and can be followed by traversing its partlist using the
  1894.    same technique.
  1895.  
  1896.    Returns:  1 on success
  1897.              0 on failure (rare, indicates serious malformatting)
  1898.  
  1899.  
  1900.    --------------------------
  1901.    void free_mime (IMIME *m);
  1902.    --------------------------
  1903.  
  1904.    Call this function when you have finished with an IMIME structure
  1905.    successfully parsed by "parse_mime". This function deallocates any
  1906.    lists or other memory allocation created to store the structure of
  1907.    the MIME message.
  1908.  
  1909.  
  1910.    -----------------------------------------------------------------
  1911.    int fake_imessage (IMESSAGE *im, char *dest, char *src, IMIME *m,
  1912.       char *boundary);
  1913.    -----------------------------------------------------------------
  1914.  
  1915.    "Sanitize" a single section of a multipart message. This function
  1916.    extracts the section of the message contained in "src" and identified
  1917.    by "m" (which is presumed to be a part from the "partlist" list of a
  1918.    multipart message) to the file named in "dest". Any necessary decoding
  1919.    and conversion is done on the section. "Boundary" should point to the
  1920.    boundary string that delimits the section in the file (you should
  1921.    usually pass "d.mpp.boundary" from the enclosing message's IMIME
  1922.    structure for this).
  1923.  
  1924.    Parsing information about the extracted section is stored in the
  1925.    IMESSAGE structure "im".
  1926.  
  1927.    It is the responsibility of the calling function to delete the file
  1928.    "dest" when it is no longer required.
  1929.  
  1930.    Returns:  1 on success
  1931.              0 on failure
  1932.  
  1933.  
  1934.    -----------------------------------------------
  1935.    int decode_mime_header (char *dest, char *src);
  1936.    -----------------------------------------------
  1937.  
  1938.    Search for and decode 8-bit data encapsulated according to RFC-1522
  1939.    rules in a string of data (presumably a header extracted from a message
  1940.    using fm_get_folded_line()). No line breaks are permitted in the input
  1941.    data.
  1942.  
  1943.      "dest" should point to a buffer where the decoded header string should
  1944.         be written. The destination buffer should be at least as large as
  1945.         the input buffer.
  1946.  
  1947.      "src" should point to the candidate string, which may contain multiple
  1948.         RFC-1522-encoded strings.
  1949.  
  1950.    Returns:  1 if encoded data was found (dest is valid)
  1951.              0 if no encoded data was found (dest is invalid)
  1952.             -1 on error (bad format or unrecognized char set)
  1953.  
  1954.  
  1955.    --------------------------------------------------------
  1956.    int encode_mime_header (char *dest, char *src, int raw);
  1957.    --------------------------------------------------------
  1958.  
  1959.    Encode a string using RFC1522 rules; transformation only occurs if the
  1960.    input string contains 8-bit data.
  1961.  
  1962.      "dest" should point to a buffer where the encoded string should be
  1963.         written. The destination buffer should be at least twice as large
  1964.         as the input buffer. Note that this routine NEVER generates
  1965.         multiple encoded blocks in the string - either the whole string is
  1966.         encoded, or none of it is. Furthermore, this routine only ever
  1967.         generates the quoted-printable RFC1522 variant, never the BASE64
  1968.         encoding, which seems to me to be a ludicrous thing to place in the
  1969.         headers of a mail message.
  1970.  
  1971.      "src"  should point to the source string, which may or may not contain
  1972.         8-bit data. "src" is expected to be in the WinANSI character set.
  1973.  
  1974.    Returns:  1 if data was encoded (dest is valid)
  1975.              0 if no data was encoded (dest is invalid)
  1976.  
  1977.  
  1978.    ----------------------------------------------------------
  1979.    int encode_base64_str (char *dest, char *src, int srclen);
  1980.    ----------------------------------------------------------
  1981.  
  1982.    Encode an arbitrary string of data in BASE64 format. Note that this
  1983.    encoding process will produce an output line approximately a third
  1984.    larger again than the input, and that MIME message conventions on
  1985.    line length are ignored - the output will be a single line. This
  1986.    function is useful for protocols like RFC2554 that require string
  1987.    data to be transmitted as a BASE64-encoded quantity.
  1988.  
  1989.       "dest" should point to a buffer where the BASE64 encoded data
  1990.          should be written. The destination buffer should be at least twice
  1991.          as large as the input buffer.
  1992.  
  1993.       "src" is the source string, which may contain any data (it is not
  1994.          limited to textual or ASCII characters).
  1995.  
  1996.       "srclen" is the length in bytes of "src".
  1997.  
  1998.    Returns   1 on success
  1999.              0 on failure (very, very rare).
  2000.  
  2001.  
  2002.    -----------------------------------------------------------
  2003.    int decode_base64_str (char *dest, char *src, char *table);
  2004.    -----------------------------------------------------------
  2005.  
  2006.    Decode a string encoded using BASE64.
  2007.  
  2008.       "dest" should point to a location where the decoded data is to
  2009.          be written. This buffer should be at least the same size as
  2010.          the input buffer. The decoded data may be binary in nature -
  2011.          there is no guarantee that it will be a nul-terminated C
  2012.          string.
  2013.  
  2014.       "src" should point to the string to decode
  2015.  
  2016.       "table" can point to a 128-byte character table used to convert
  2017.          high-bit characters in the decoded data to an alternative
  2018.          character set. This value will typically be NULL.
  2019.  
  2020.    Returns:  1 on success
  2021.              0 on failure (malformatted BASE64 data)
  2022.  
  2023.  
  2024.  
  2025. 5.6 - Message composition routines
  2026.  
  2027. Mercury provides an easy way to create relatively complex mail messages
  2028. in MIME formats. You can create simple messages, messages with
  2029. attachments, multipart/alternative messages containing different versions
  2030. of the same data, and MIME digests containing other mail messages.
  2031.  
  2032.  
  2033.    -------------------------------------------------------
  2034.    void *om_create_message (UINT_32 mtype, UINT_32 flags);
  2035.    -------------------------------------------------------
  2036.  
  2037.    Create a message. The message type is set to "mtype" and the message
  2038.    flags are set to "flags". The handle returned by this function must be
  2039.    passed to all subsequent message building functions. Messages created
  2040.    by Mercury are always valid MIME messages - non-MIME message types can
  2041.    not be created (nor is it any longer desirable to do so).
  2042.  
  2043.    "mtype" must be one of the following values:
  2044.  
  2045.       OM_MT_PLAIN         - A simple, single-part text/plain message
  2046.       OM_MT_MULTIPART     - A multipart/mixed message
  2047.       OM_MT_ALTERNATIVE   - A multipart/alternative message
  2048.       OM_MT_DIGEST        - A multipart/digest message
  2049.  
  2050.    "flags" is a bitmap composed of the following possible values:
  2051.  
  2052.       OM_M_8BIT           - The message body contains 8-bit data
  2053.  
  2054.    The "flags" field can be set later using the "om_add_field" function
  2055.    and the OM_MF_FLAGS selector.
  2056.  
  2057.    Returns:    Non-NULL on success (message handle)
  2058.                NULL on failure
  2059.  
  2060.  
  2061.    ------------------------------------------
  2062.    INT_32 om_dispose_message (void *mhandle);
  2063.    ------------------------------------------
  2064.  
  2065.    Release the storage used by a message. It is an error to use the
  2066.    "mhandle" parameter after it has been passed to this routine. This
  2067.    function must be called when the message is no longer needed, to
  2068.    deallocate internal buffers.
  2069.  
  2070.    It is the calling routine's responsibility to deal with the body file
  2071.    and any files attached to the message.
  2072.  
  2073.    Returns:    > 0 on success
  2074.                = 0 on failure (invalid handle)
  2075.  
  2076.  
  2077.    ------------------------------------------------------------------
  2078.    INT_32 om_add_field (void *mhandle, UINT_32 selector, char *data);
  2079.    ------------------------------------------------------------------
  2080.  
  2081.    Add a field to a message.
  2082.  
  2083.    "selector" can be any one of the following values:
  2084.  
  2085.       OM_MF_TO       - set the master recipient of the message
  2086.       OM_MF_SUBJECT  - set the subject field for the message
  2087.       OM_MF_CC       - set the secondary recipients of the message
  2088.       OM_MF_FROM     - set the originator of the message.
  2089.       OM_MF_BODY     - set the filename containing the message body
  2090.       OM_MF_RAW      - add a raw header for the message.
  2091.       OM_MF_FLAGS    - set the message's global flags
  2092.  
  2093.    If the "OM_MF_FROM" field is not set for a message, it will default
  2094.    to the postmaster address for the system. It is an error to attempt
  2095.    to send a message for which "OM_MF_TO" has not been set; all other
  2096.    fields are optional.
  2097.  
  2098.    The "OM_MF_RAW" selector allows the calling process to add any header
  2099.    it wishes to the message. The header should be passed complete,
  2100.    including the header keyword, but without a terminating CRLF pair.
  2101.    Mercury guarantees that headers added to a message will be written to
  2102.    the message in the order in which they are added, so you can add
  2103.    headers with continuations if you wish. You cannot add any of the
  2104.    following headers using "OM_MF_RAW": "To", "Cc", "Subject", "BCC",
  2105.    "Content-type", "Content-transfer-encoding", "MIME-Version", or
  2106.    "Content-disposition". You can reset all custom headers added using
  2107.    OM_MF_RAW by using OM_MF_RAW as a selector and passing NULL for the
  2108.    "data" parameter.
  2109.  
  2110.    It is explicitly legal to add a field you have already added to a
  2111.    message. This allows you to create a message then send it to multiple
  2112.    recipients in separate jobs, for example.
  2113.  
  2114.    Returns:    > 0 on success
  2115.                = 0 on error
  2116.  
  2117.  
  2118.    -----------------------------------------------------
  2119.    INT_32 om_add_attachment (void *mhandle, char *fname,
  2120.       char *ftype, char *description, UINT_32 encoding,
  2121.       UINT_32 flags, void *reserved);
  2122.    -----------------------------------------------------
  2123.  
  2124.    Add an attachment or part to a message.
  2125.  
  2126.       "fname"          - the fully-qualified path to the file to attach
  2127.       "ftype"          - a content-type string - see below
  2128.       "description"    - optional free text description string
  2129.       "encoding"       - encoding method - see below
  2130.       "flags"          - special settings for the attachment - see below
  2131.       "reserved"       - must be 0
  2132.  
  2133.    The "ftype" string is a free text atom and can have any value, provided
  2134.    it does not contain spaces, tabs or commas.
  2135.  
  2136.    "encoding" can have the following values
  2137.  
  2138.       OM_AE_DEFAULT  - default encoding (MIME BASE64 encoding)
  2139.       OM_AE_TEXT     - simple textual data, unencoded
  2140.       OM_AE_UUENCODE - uuencoding
  2141.       OM_AE_BINHEX   - Macintosh Binhex format (data fork only)
  2142.  
  2143.    "flags" can have the following values
  2144.  
  2145.       OM_AF_INLINE   - write the file as a simple textual section
  2146.       OM_AF_MESSAGE  - write the message as a Message/RFC822 part
  2147.  
  2148.    The file need not exist at the time it is attached.
  2149.  
  2150.    Mercury/32 guarantees that attachments will appear in the message in
  2151.    the order in which they were attached, so you can create messages
  2152.    that depend on the sequence of attachments with confidence.
  2153.  
  2154.    Returns:    > 0 on success
  2155.                = 0 on failure.
  2156.  
  2157.  
  2158.    -----------------------------------------------------
  2159.    INT_32 om_write_message (void *mhandle, char *fname);
  2160.    -----------------------------------------------------
  2161.  
  2162.    Given a message handle that has been populated with fields, body and
  2163.    attachments, create a simple disk file containing the final form of
  2164.    the mail message.
  2165.  
  2166.    Note that this function does *not* create a job, nor does it send
  2167.    the message. It simply renders the message structures into their
  2168.    final form.
  2169.  
  2170.    This routine is called internally by "om_send_message" to create the
  2171.    final form of the Mercury queue job. It is guaranteed that calling
  2172.    this function then calling "om_send_message" will generate a file
  2173.    and a job that are identical (so, this routine can be used to create
  2174.    archive copies of outgoing messages).
  2175.  
  2176.    Returns:        > 0 on success
  2177.                    = 0 on failure
  2178.  
  2179.  
  2180.    ------------------------------------------------------
  2181.    void *om_send_message (void *mhandle, char *envelope);
  2182.    ------------------------------------------------------
  2183.  
  2184.    Given a message handle that has been populated with fields, body
  2185.    and attachments, create a job in the Mercury queue containing the
  2186.    final form of the mail message.
  2187.  
  2188.    "envelope" is an optional envelope ("Return-path") address for the
  2189.    created message. If NULL or 0-length, the envelope will default to
  2190.    the From field, or the postmaster address.
  2191.  
  2192.    The return from this job is an open Mercury job handle. It is the
  2193.    responsibility of the calling process to make any last-minute job
  2194.    settings, then to close or discard the job.
  2195.  
  2196.    Returns:    Open Mercury job handle on success
  2197.                0 on failure
  2198.  
  2199.  
  2200. Notes on composing messages:
  2201.  
  2202. 1: To create a MIME digest, you must ensure that all the files you attach
  2203. to your message are validly-formatted RFC822 mail messages, and that each
  2204. has the OM_AF_MESSAGE bit set in its "flags" field. The message body is
  2205. ignored for MIME digests, so you may wish to consider creating a "dummy"
  2206. first message containing a summary, subscription information or other
  2207. information useful to the recipient.
  2208.  
  2209. 2: The message body file and any attachments must be closed when either
  2210. "om_write_message" or "om_send_message" is called.
  2211.  
  2212. 3: If the "OM_M_8BIT" flag is set for the message, the body of the
  2213. message will be sent using the MIME "quoted-printable" encoding scheme.
  2214. At present, only the ISO-8859-1 character set (which is the same as the
  2215. default MS-Windows character set) is supported for 8-bit data. It is
  2216. safe to set the OM_M_8BIT flag for a message that contains only 7-bit
  2217. data, but it is an error not to set it for a message that *does* contain
  2218. 8-bit character data.
  2219.  
  2220. 4: Address fields ("To:" and "Cc:") can be up to 32000 bytes in length,
  2221. and may contain multiple addresses separated by commas. Mercury/32 will
  2222. wrap the address fields according to RFC822 line folding rules. Mercury
  2223. will *not* expand partial addresses to fully-qualified domain name forms.
  2224. It is up to your Daemon to ensure that all addresses are legal and
  2225. fully-qualified.
  2226.  
  2227.  
  2228.  
  2229. 5.7 - Statistics and logging interface
  2230.  
  2231. Mercury incorporates a comprehensive Statistics Manager that can keep
  2232. running statistics and information about the throughput, performance and
  2233. operation of the system. The entire Statistics Manager interface is
  2234. available to Daemons and protocol modules. Mercury also provides a System
  2235. Messages window into which console-type messages can be written at
  2236. various priority levels.
  2237.  
  2238.  
  2239.    ----------------------------------------------
  2240.    INT_32 st_register_module (char *module_name);
  2241.    ----------------------------------------------
  2242.  
  2243.    Register a module with the statistics interface. "Module name" is the
  2244.    name that should be presented in the statistics manager list for this
  2245.    item. This function creates a top-level statistics category in the
  2246.    Statistics Manager's list.
  2247.  
  2248.    Returns:    Module handle > 0 on success
  2249.                0 on failure (very rare)
  2250.  
  2251.  
  2252.    ---------------------------------------------
  2253.    INT_32 st_unregister_module (INT_32 mhandle);
  2254.    ---------------------------------------------
  2255.  
  2256.    "Unregister" a module and remove its entries from the statistics list.
  2257.  
  2258.    Returns     1 on success
  2259.                0 on failure (not registered)
  2260.  
  2261.  
  2262.    ----------------------------------------------------------
  2263.    INT_32 st_create_category (INT_32 mhandle, char *cname,
  2264.       INT_32 ctag, INT_32 ctype, INT_32 dlen, UINT_32 flags);
  2265.    ----------------------------------------------------------
  2266.  
  2267.    Create a single statistical category within a specified module.
  2268.  
  2269.       "mhandle"  module's registered handle
  2270.  
  2271.       "cname"    display name for this category
  2272.  
  2273.       "ctag"     module's reference handle for this item; this is an
  2274.                  arbitrary value supplied by the module that identifies
  2275.                  this category, and it must be unique within the items
  2276.                  created within a single module.
  2277.  
  2278.       "ctype"    type of data represented
  2279.                  - STC_INTEGER for integral data
  2280.                  - STC_STRING  for string data
  2281.                  - STC_DATE    for time/date data
  2282.  
  2283.       "dlen"     maximum size of data (for strings)
  2284.  
  2285.       "flags"    attributes of this category
  2286.                  - STF_CUMULATIVE   data accumulates
  2287.                  - STF_PEAK         record only the highest value
  2288.                  - STF_UNIQUE       allow only one named instance
  2289.  
  2290.    Returns:    > 0 (category handle) on success
  2291.                  0 on failure
  2292.  
  2293.  
  2294.    ---------------------------------------------------------
  2295.    INT_32 st_remove_category (INT_32 mhandle, UINT_32 ctag);
  2296.    ---------------------------------------------------------
  2297.  
  2298.    Remove a statistical category and delete it from any lists currently
  2299.    displayed. "ctag" should be the category tag handle passed when the
  2300.    category was created.
  2301.  
  2302.    Returns      1 on success
  2303.                 0 on failure (category not found)
  2304.  
  2305.  
  2306.    -------------------------------------------------------
  2307.    INT_32 st_set_hcategory (INT_32 chandle, UINT_32 data);
  2308.    -------------------------------------------------------
  2309.  
  2310.    Set the data for a category given its category handle only. Note that
  2311.    a "category handle" is the return from st_create_category, not the
  2312.    ctag value passed to that function. The system guarantees that
  2313.    category handles will always be globally unique, and may change from
  2314.    session to session.
  2315.  
  2316.    "data" must be of a type appropriate for the registered category. If
  2317.    the category has the "STF_CUMULATIVE" attribute set, then the data are
  2318.    accumulated to any data already existing for the category.
  2319.  
  2320.    Returns:     > 0 on success
  2321.                   0 on failure
  2322.  
  2323.  
  2324.    -------------------------------------------------------------------
  2325.    INT_32 st_set_category (INT_32 mhandle, INT_32 ctag, UINT_32 data);
  2326.    -------------------------------------------------------------------
  2327.  
  2328.    Set the data for a category given its module handle and the module's
  2329.    internal category handle. "data" must be of a type appropriate for the
  2330.    registered category. If the category has the "STF_CUMULATIVE" attribute
  2331.    set, then the data are accumulated to any data that already exist for
  2332.    the category.
  2333.  
  2334.    Returns:     > 0 on success
  2335.                   0 on failure
  2336.  
  2337.  
  2338.    ----------------------------------------------------------
  2339.    INT_32 st_get_next_module (INT_32 mhandle, char *modname);
  2340.    ----------------------------------------------------------
  2341.  
  2342.    Return the next module in the statistics list. To enumerate the modules
  2343.    in the list, set "mhandle" to -1 on the first call, then pass the return
  2344.    value in subsequent calls until -1 is returned.
  2345.  
  2346.    The module's name is copied into "modname", which should be at least 128
  2347.    characters in length.
  2348.  
  2349.    Returns:     > 0 on success
  2350.                  -1 on no more modules.
  2351.  
  2352.  
  2353.    -------------------------------------------------------------
  2354.    INT_32 st_get_next_category (INT_32 mhandle, INT_32 chandle,
  2355.       char *cname, INT_32 *ctype, INT_32 *clen, INT_32 *cflags);
  2356.    -------------------------------------------------------------
  2357.  
  2358.    Return the next category belonging to the module "mhandle" in the
  2359.    statistics list. To enumerate all the categories in the category list,
  2360.    pass the module handle (returned by "st_get_next_module", set "chandle"
  2361.    to -1 on the first call then pass the return value in subsequent calls.
  2362.    Repeat until the function returns -1.
  2363.  
  2364.       "cname" receives the category's descriptive text; allocate
  2365.          at least 128 characters
  2366.  
  2367.       "ctype" receives the type of the category's data; possible
  2368.          values are STC_INTEGER, STC_STRING or STC_DATE
  2369.  
  2370.       "clen" receives the maximum length of the data type.
  2371.  
  2372.       "cflags" receives the flags associated with the data item;
  2373.          possible bit values are STF_CUMULATIVE, STF_PEAK and
  2374.          STF_UNIQUE.
  2375.  
  2376.    Returns:     > 0 on success
  2377.                  -1 on no more categories
  2378.  
  2379.  
  2380.    ---------------------------------------------------------
  2381.    INT_32 st_get_category_data (INT_32 chandle, void *data);
  2382.    ---------------------------------------------------------
  2383.  
  2384.    Copy the current data value for the specified category into the
  2385.    location pointed to by "data". It is the calling module's
  2386.    responsibility to ensure that sufficient space is allocated. For
  2387.    STC_INTEGER types, you should allocate 4 bytes; for STC_DATE types,
  2388.    you should allocate 4 bytes (the returned value is a C "time_t"
  2389.    type); for STC_STRING types, you should allocate the "clen" returned
  2390.    by a call to st_get_next_category (which will always be correct for
  2391.    integer and date types as well).
  2392.  
  2393.    Returns:       1 on succes
  2394.                   0 on failure
  2395.  
  2396.  
  2397.    --------------------------------------------------------------------
  2398.    INT_32 st_export_stats (INT_32 mhandle, char *fname, UINT_32 flags);
  2399.    --------------------------------------------------------------------
  2400.  
  2401.    Export a single module's statistics, or all modules' stats
  2402.    to the file named in "fname" in plain text format.
  2403.  
  2404.       "mhandle"  The module handle for which statistics should
  2405.                  be exported. If 0, export all modules.
  2406.  
  2407.       "fname"    Filename to receive data
  2408.  
  2409.       "flags"    If (flags & 1), append data to the file
  2410.                  If (flags & 2), omit 0 or undefined categories
  2411.  
  2412.    Returns       1 on success
  2413.                  0 on failure
  2414.  
  2415.  
  2416.  
  2417.    ----------------------------------------------------------
  2418.    void logstring (INT_16 ltype, INT_16 priority, char *str);
  2419.    ----------------------------------------------------------
  2420.  
  2421.    Log a message in the System Messages window
  2422.  
  2423.       "ltype"    An arbitrary integer representing the "type" of
  2424.                  the message. It is a convention that "major" types
  2425.                  should be divisible by 10; other types of data
  2426.                  (minor types) will be indented.
  2427.  
  2428.       "priority" The level of importance of the message. The user
  2429.                  can set the level they want displayed, and messages
  2430.                  with a lower priority than that will be discarded.
  2431.                  Possible values for this field are:
  2432.                  - LOG_DEBUG
  2433.                  - LOG_INFO
  2434.                  - LOG_NORMAL
  2435.                  - LOG_SIGNIFICANT
  2436.                  - LOG_URGENT
  2437.                  - LOG_NONE
  2438.  
  2439.    Developers are strongly urged to use priority values correctly and
  2440.    responbsibly, for the benefit of users.
  2441.  
  2442.  
  2443.    -------------------------------------------------------------
  2444.    void logdata (INT_16 ltype, INT_16 priority, char *fmt, ...);
  2445.    -------------------------------------------------------------
  2446.  
  2447.    The same as "logstring", but supporting "sprintf"-type formatting.
  2448.    See the documentation for "sprintf" in your compiler manual for
  2449.    information on valid formatting codes.
  2450.  
  2451.    Note that because of the way macros are handled in the C language,
  2452.    this function cannot be accessed using the convenience macros in
  2453.    "daemon.h" - you must access the function directly from the protocol
  2454.    block structure.
  2455.  
  2456.  
  2457.  
  2458. Appendix A - Technical issues
  2459.  
  2460. A.1 - Function names
  2461.  
  2462. When Mercury loads a Daemon, it attempts to locate the functions exported
  2463. by the Daemon using the function names. Different compilers may export
  2464. the names of your functions in different ways - even though your function
  2465. may be called "startup", the compiler may actually export it as
  2466. "_startup", for historical reasons.
  2467.  
  2468. Mercury always attempts to locate your functions in two ways: the first
  2469. attempt prepends a "_" to the function name, while the second searches
  2470. for the function name with no leading "_", all in uppercase. The first of
  2471. these methods will reliably detect functions exported by Borland
  2472. compilers, while the second mechanism will usually detect functions
  2473. exported by the Microsoft Visual C++ compiler family.
  2474.  
  2475. If you find that your functions do not seem to be getting called by
  2476. Mercury, check on the format of the name exported by your compiler - this
  2477. is the most likely source of the problem.
  2478.  
  2479.